文字阴影

<style name="">
    <item name="android:shadowColor">#7f000000</item>
    <item name="android:shadowDx">0</item>
    <item name="android:shadowDy">0</item>
    <item name="android:shadowRadius">5</item>
</style>

Anroid 小记(dp->dx,measure(),PagerAdapter)

android:typeface="monospace"
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 6, getResources().getDisplayMetrics());
View childView = createChildView(inflater, resources, BoothCenterItemData.defaultData[0]);
int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
childView.measure(widthMeasureSpec, heightMeasureSpec);
int measuredHeight = childView.getMeasuredHeight();
public class BoothCenterAdapter extends PagerAdapter {
    private final List<BoothCenterItemView> itemViews;

    public BoothCenterAdapter(List<BoothCenterItemView> itemViews) {
        this.itemViews = itemViews;
    }

    @NonNull
    @Override
    public Object instantiateItem(@NonNull ViewGroup container, int position) {
        View view = itemViews.get(position);
        container.addView(view);
        return view;
    }

    @Override
    public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
        container.removeView((View) object);
    }

    @Override
    public int getCount() {
        return itemViews.size();
    }

    @Override
    public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
        return view == object;
    }
}

Android签名

1、创建

keytool -genkey -keystore [签名文件]  -alias bieming -keyalg RSA -validity 10000

2、查看

keytool -list -keystore  [签名文件]

注:当密码中有特殊符号时:keytool -list -v -keystore android.keystore -storepass ‘112&He’

3、修改别名

keytool -changealias -keystore android.keystore -alias [当前别名] -destalias [新别名]

EditText光标问题

1、在Edittext中加入以下属性
android:cursorVisible=”true”
android:textCursorDrawable=”@null”

2、在Edittext中加入以下属性
android:cursorVisible=”true”
android:textCursorDrawable=”@drawable/test_cursor”

对应的drawable文件

<?xml version=”1.0″ encoding=”utf-8″?>
  <shape xmlns:android=”http://schemas.android.com/apk/res/android
    android:shape=”rectangle”>
    <size android:width=”1dp” />
    <span style=”font-family: Arial, Helvetica, sans-serif;”>
   <!– 光标宽度可以自己定义 –></span>
    <solid android:color=”#008000″ />
   <!– 光标颜色可以自己定义 –>
  </shape>

3、如果以上没有效果就请用这个

明确指定EditText的inputType属性值inputType属性中的textCapSentences
不要用这个,国内手机好像没有用到这个,个人证实而已,用text或者textMultiLine
android:inputType=”text|textMultiLine”

Android 字符串测绘参考值

private void drawText(Canvas canvas, int x, int y, int width, int height){
        Paint.FontMetrics fontMetrics = paintText.getFontMetrics();

        canvas.drawText("TESThf我y", x, y, paintText);

        int line1 = y;
        int line2 = (int) (line1 + fontMetrics.top);
        int line3 = (int) (line1 + fontMetrics.ascent);
        int line4 = (int) (line1 + fontMetrics.descent);
        int line5 = (int) (line1 + fontMetrics.leading);
        int line6 = (int) (line1 + fontMetrics.bottom);

        // line2 top = y + top
        canvas.drawLine(0, line2, width, line2, linePaint);
        canvas.drawLine(20, line2, 20 + 100, line2 - 100, linePaint);
        canvas.drawText("top:" + fontMetrics.top, 100 + 20, line2 - 100, linePaint);

        // line3 ascent = y + ascent 上升
        canvas.drawLine(0, line3, width, line3, linePaint);
        canvas.drawLine(40, line3, 40 + 100, line3 - 100, linePaint);
        canvas.drawText("ascent:" + fontMetrics.ascent, 100 + 40, line3 - 100, linePaint);

        // line1 base = y
        canvas.drawLine(0, line1, width, line1, linePaint);
        canvas.drawLine(60, line1, 60 + 100, line1 + 100, linePaint);
        canvas.drawText("Base", 60 + 100, line1 + 100, linePaint);

        // line4 descent = y + descent 下降
        canvas.drawLine(0, line4, width, line4, linePaint);
        canvas.drawLine(0, line4, 100, line4 + 100, linePaint);
        canvas.drawText("descent:" + fontMetrics.descent, 100, line4 + 100, linePaint);

        // line5 leading = y + leading 行距
        canvas.drawLine(0, line5, width, line5, linePaint);
        canvas.drawLine(200, line5, 200 + 100, line5 + 100, linePaint);
        canvas.drawText("leading:" + fontMetrics.leading, 200 + 100, line5 + 100, linePaint);

        // line6 bottom = y + bottom
        canvas.drawLine(0, line6, width, line6, linePaint);
        canvas.drawLine(400, line6, 400 + 100, line6 + 100, linePaint);
        canvas.drawText("bottom:" + fontMetrics.bottom, 400 + 100, line6 + 100, linePaint);
    }

 

自定义Drawable,实现“原创”文本Table效果

1.自定义Drawable,绘制“原创”文本字符串

public class LabelDrawable extends Drawable {
    private String tag;
    private Paint paint;
    private String labelText;
    private float textSize;
    private float labelWidth;
    private float labelHeight;
    private float leftPadding;
    private float rightPadding;
    private float labelLeftRightPadding;
    private float labelTopBottomPadding;
    private float borderWidth;
    private float round;

    private float strokeWidth;

    public LabelDrawable(String tag, String labelText, float textSize) {
        this.tag = tag;
        this.labelText = labelText;
        this.textSize = textSize;
        this.paint = new Paint();
        this.paint.setColor(0xffff6f00);
        this.paint.setAntiAlias(true);

        this.paint.setTextSize(textSize);
        this.labelWidth = this.paint.measureText(labelText);
        this.labelHeight = this.paint.descent() - this.paint.ascent();

        this.strokeWidth = this.paint.getStrokeWidth();
    }

    public void setLeftPadding(float leftPadding) {
        this.leftPadding = leftPadding;
    }

    public void setRightPadding(float rightPadding) {
        this.rightPadding = rightPadding;
    }

    public void setLabelLeftRightPadding(float labelLeftRightPadding) {
        this.labelLeftRightPadding = labelLeftRightPadding;
    }

    public void setLabelTopBottomPadding(float labelTopBottomPadding) {
        this.labelTopBottomPadding = labelTopBottomPadding;
    }

    public void setBorderWidth(float borderWidth) {
        this.borderWidth = borderWidth;
    }

    public void setRound(float round) {
        this.round = round;
    }

    @Override
    public void draw(@NonNull Canvas canvas) {
        Rect bounds = getBounds();
        Log.w("zzh", tag + " bounds:" + bounds);

        Paint.FontMetrics fontMetrics = this.paint.getFontMetrics();

        float x = bounds.left + leftPadding + borderWidth + labelLeftRightPadding;
        float y = bounds.top + borderWidth + labelTopBottomPadding + Math.abs(fontMetrics.ascent);// - fontMetrics.descent;
        Log.w("zzh", tag + " x:" + x + " y:" + y);

        paint.setTextSize(textSize);
        paint.setStyle(Paint.Style.FILL);
        paint.setStrokeWidth(this.strokeWidth);
        canvas.drawText(labelText, x, y, paint);

        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(borderWidth);

        RectF borderRect = new RectF();
        borderRect.left     = bounds.left + leftPadding;
        borderRect.top      = bounds.top + labelTopBottomPadding;
        borderRect.right    = bounds.right - rightPadding;
        borderRect.bottom   = bounds.bottom - labelTopBottomPadding;

        canvas.drawRoundRect(borderRect, round, round, paint);

        Log.w("zzh", tag + " bounds:" + borderRect);
    }

    @Override
    public void setAlpha(int alpha) {
        this.paint.setAlpha(alpha);
    }

    @Override
    public void setColorFilter(@Nullable ColorFilter colorFilter) {

    }

    @Override
    public int getOpacity() {
        return PixelFormat.TRANSLUCENT;
    }

    // 固有的大小
    @Override
    public int getIntrinsicHeight() {
        return (int) (borderWidth + labelTopBottomPadding + labelHeight + labelTopBottomPadding + borderWidth);
    }

    @Override
    public int getIntrinsicWidth() {
        return (int) (leftPadding + borderWidth + labelLeftRightPadding
                + labelWidth +
                labelLeftRightPadding + borderWidth + rightPadding);
    }
}

2、调整Drawable和文本的对齐

public class CenterAlignImageSpan extends ImageSpan {

    public CenterAlignImageSpan(Drawable drawable) {
        super(drawable);
    }

    @Override
    public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom,
                     @NonNull Paint paint) {
        Drawable b = getDrawable();
        Paint.FontMetricsInt fm = paint.getFontMetricsInt();
        int transY = (y + fm.descent + y + fm.ascent) / 2 - b.getBounds().bottom / 2;//计算y方向的位移
        canvas.save();
        canvas.translate(x, transY);//绘制图片位移一段距离
        b.draw(canvas);
        canvas.restore();
    }
}

3、写个帮助类,协调各类

public class LabelTextViewHelper{
    private TextView textView;
    private final float textSize;
    private final float leftPadding;
    private final float rightPadding;
    private final float borderWidth;
    private final float round;
    private final float labelLeftRightPadding;
    private final float labelTopBottomPadding;

    public LabelTextViewHelper(@NonNull TextView textView) {
        this.textView = textView;

        DisplayMetrics displayMetrics = textView.getResources().getDisplayMetrics();

        textSize        = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 13, displayMetrics);
        leftPadding     = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, displayMetrics);
        rightPadding    = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8, displayMetrics);
        borderWidth     = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, displayMetrics);
        round           = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, displayMetrics);
        labelLeftRightPadding = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 6, displayMetrics);
        labelTopBottomPadding = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4, displayMetrics);
    }

    public void setLabelAndText(@NonNull String label, @NonNull String text){
        SpannableString sp = new SpannableString(label + text);

        //获取一张图片
        LabelDrawable drawable = new LabelDrawable("TextView", label, textSize);
        drawable.setLeftPadding(leftPadding);
        drawable.setRightPadding(rightPadding);
        drawable.setLabelLeftRightPadding(labelLeftRightPadding);
        drawable.setLabelTopBottomPadding(labelTopBottomPadding);
        drawable.setBorderWidth(borderWidth);
        drawable.setRound(round);
        drawable.setBounds(0, 0, drawable.getMinimumWidth(), drawable.getMinimumHeight());

        //居中对齐imageSpan
        ImageSpan imageSpan = new CenterAlignImageSpan(drawable);
        sp.setSpan(imageSpan, 0, label.length(), ImageSpan.ALIGN_BASELINE);

        textView.setText(sp);
    }
}

4、实际使用

String labelText = "原创";

TextView textView = findViewById(R.id.main_txt);
textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18);
LabelTextViewHelper helper = new LabelTextViewHelper(textView);
helper.setLabelAndText(labelText, "10个高效的有氧减肥动作,快速瘦成一道闪电");

 

AndroidStudio 3.0 implementation、api

api 指令 完全等同于compile指令

implementation指令
使用了这个命令编译的依赖,对该项目由依赖的项目将无法访问到使用该命令编译的依赖,也就是将该依赖隐藏在内部,而不对外部公开。
简单的说,就是使用implementation指令的依赖不会传递

Android Studio 报错显示 mips64el-linux-android-strip 找不到

Android/Sdk/ndk-bundle/toolchains/mips64el-linux-android-4.9/prebuilt/linux-x86_64/bin/mips64el-linux-android-strip 找不到, 导致编译报错

也就是说在Android/Sdk/ndk-bundle/toolchains/mips64el-linux-android-4.9/prebuilt/linux-x86_64/ 路径下找不到 mips64el-linux-android-strip
如果ndk版本在r17版, 很有可能出现这个问题. 我的解决方案是:

  1. 先清除 Android/Sdk/ndk-bundle/ 下的内容
  2. https://developer.android.google.cn/ndk/downloads/older_releases 下载16b版本的ndk到本地, 并解压说, 将解压缩后的所有文件拷贝到 Android/Sdk/ndk-bundle/ 目录下
  3. 重新build工程

DynamicLoadApk 运行流程跟踪

一、解析插件Apk

DLPluginManager pluginManager = DLPluginManager.getInstance(BaseApplication.getInstance());

DLPluginPackage pluginPackage = pluginManager.loadApk(pluginApkPath);
DLIntent intent = new DLIntent(pluginPackage.packageName, pluginPackage.defaultActivity);

pluginManager.startPluginActivity(this, intent);

1、loadApk方法

public DLPluginPackage loadApk(final String dexPath, boolean hasSoLib){
    mFrom = DLConstants.From_external;
    
    int packageInfoFlags = PackageManager.Get_activities | PackageManager.Get_services;
    PackageInfo packageInfo = mContext.getPackageManager().getPackageArchiveInfo(dexPath, packageInfoFlags);
    if(packageInfo == null) return null;

    DLPluginPackage pluginPackage = preparePluginEnv(packageInfo, dexPath);
    if(hasSoLib){
        copySoLib(dexPath);
    }

    return pluginPackage;
}

2、preparePluginEnv方法

private DLPluginPackage preparePluginEnv(PackageInfo packageInfo, String dexPath){
    DLPluginPackage pluginPackage = mPackagesHolder.get(packageInfo.packageName);
    if(pluginPackage != null) return pluginPackage;

    DexClassLoader dexClassLoader = createDexClassLoader(dexPath);
    AssetManager assetManager = createAssetManager(dexPath);
    Resources resources = createResources(assetManager);

    // create PluginPackage
    pluginPackage = new DLPluginPackage(dexClassLoader, resources, packageInfo);
    mPackageHolder.put(packageInfo.packageName, pluginPackage);

    return pluginPackage;
}

private DexClassLoader createDexClassLoader(String dexPath){
    File dexOutputDir = mContext.getDir("dex", Context.Mode_private);
    dexOutputPath = dexOutputDir.getAbsolutePath();

    DexClassLoader loader = new DexClassLoader(
        dexPath,
        dexOutputPaht,
        mNativeLibDir,
        mContext.getClassLoader()
    );

    return loader;
}

private AssetManager createAssetManager(String dexPath){
    try{
        AssetManager assetManager = AssetManager.class.newInstance();
        Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);
        addAssetPath.invoke(assetManager, dexPath);
        return assetManager;
    }catch(Exception e){}
}

private Resources createResources(AssetManager assetManager){
    Resources superRes = mContext.getResources();
    Resources resources = new Resources(
        assetManager,
        superRes.getDisplayMetrics(),
        superRes.getConfiguration()
    );

    return resources;
}

二、运行插件Apk

启动代理的Activity,分别可以:在DLPluginManager里调用startPluginActivityForResult(一般宿主使用),还可以在DLPluginActivity里使用startPluginActivity(在插件里使用)。

DLIntent intent = new DLIntent(getPackageName(), SecondActivity.class);
startPluginActivity(intent);

上面是调用Activity方法,再往下看:

public int startPluginActivity(DLIntent dlIntent){
    return startPluginActivityForResult(dlIntent, -1);
}

/** @return may be 
    {@link #Start_result_success}, 
    {@link #Start_result_no_pkg},  
    {@link #Start_result_no_class}, 
    {@link #Start_result_type_error} */
public int startPluginActivityForResult(DLIntent dlIntent, int requestCode){
    if(mFrom == DLConstants.From_external){
        if(dlIntent.getPluginPackage() == null){
            dlIntent.setPluginPackage(mPluginPackage.packageName);
        }
    }

    return mPluginManager.startPluginActivityForResult(that, dlIntent, requestCode);
}

之后交给PluginManager启动Activity:

public int startPluginActivityForResult(Context context, DLIntent dlIntent, int requestCode){
    if(mFrom == DLConstants.From_internal){
        dlIntent.setClassName(context, dlIntent.getPluginClass());
        preformStartActivityForResult(context, dlIntent, requestCode);
        return DLPluginManager.Start_result_success;
    }

    String packageName = dlIntent.getPluginPackage();
    if(TextUtils.isEmpty(packageName)){
        throw new NullPointerException("disallow null packageName.");
    }

    DLPluginPackage pluginPackage = mPackageHolder.get(packageName);
    if(pluginPackage == null){
        return Start_result_no_pkg;
    }

    final String className = getPluginActivityFullPath(dlIntent, pluginPackage);
    Class<?> clazz = loadPluginClass(pluginPackage.classLoader, className);
    if(clazz == null){
        return Start_result_no_class;
    }

    Class<? extends Activity> activityClass = getProxyActivityClass(clazz);
    if(activityClass == null){
        return Start_result_type_error;
    }

    dlIntent.putExtra(DLConstants.Extra_class, className);
    dlIntent.putExtra(DLConstants.Extra_package, packageName);
    dlIntent.setClass(mContext, activityClass);
    performStartActivityForResult(context, dlIntent, requestCode);
    return Start_result_success;
}

先进行一些判断,然后获取要启动的Activity的全称,然后通过函数loadPluginClass()利用反射获取插件实例activityClass

函数loadPluginClass:

private Class<?> loadPluginClass(ClassLoader classLoader, String className){
    Class<?> clazz = null;
    try{
        clazz = Class.forName(className, true, classLoader);
    }catch(ClassNotFoundException e){}
    return clazz;
}

通过函数getProxyActivityClass(),根据插件实例获取它对应在宿主里对应的代理类:

private Class<? extends Activity> getProxyActivityClass(Class<?> clazz){
    Class<? extends Activity> activityClass = null;
    if(DLBasePluginActivity.class.isAssignableFrom(clazz)){
        activityClass = DLProxyActivity.class;
    }else if(DLBasePluginFragmentActivity.class.isAssignableFrom(clazz)){
        activityClass = DLProxyFragmentActivity.class;
    }else if(Activity.class.isAssignableFrom(clazz)){
        activityClass = Activity.class;
    }
    return activityClass;
}

当这一步基本上完成偷天换柱的任务,表面上要启动的是插件类,其实启动的是一个宿主代理类。
在把插件包名和要启动的插件类名放到Intent中,传递一个代理类,最后直接启动代理Activity就好了。

private void performStartActivityForResult(Context context, DLIntent dlIntent, int requestCode){
    if(context instanceof Activity){
        ((Activity)context).startActivityForResult(dlIntent, requestCode);
    }else{
        context.startActivity(dlIntent);
    }
}

三、通过代理类运行插件类

在这里分析DLProxyActivity,经前面分析,已经启动了代理类,当然就执行到代理类的onCreate了:

protected DLPlugin mRemoteActivity;
private DLProxyImpl impl = new DLProxyImpl(this);

@Override
protected void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    impl.onCreate(getIntent());
}

接着流程就跑到DLProxyImpl类的onCreate()方法中:

public void onCreate(Intent intent){
    intent.setExtrasClassLoader(DLConfigs.sPluginClassLoader);

    mPackageName = intent.getStringExtra(DLConstants.Extra_package);
    mClass = intent.getStringExtra(DLConstants.Extra_class);

    mPluginManager = DLPluginManager.getInstance(mProxyActivity);
    mPluginPackage = mPluginManager.getPackage(mPackageName);
    mAssetManager = mPluginPackage.assetManager;
    mResources = mPluginPackage.resources;

    initializeActivityInfo();
    handleActivityInfo();
    launchTargetActivity();
}
1、在initializeActivityInfo()之前是获取intent传递过来的插件包名、插件类,并根据它们获取插件信息PluginPackage
2、PluginPackage里面存放了插件的resource和assetManager
3、initializeActivityInfo()和handleActivityInfo()主要是进行一些主题处理
4、重点在launchTargetActivity()方法,初始化插件类
protected void launchTargetActivity(){
    try{
        Class<?> localClass = getClassLoader().loadClass(mClass);
        Constructor<?> localConstructor = localClass.getConstructor(new Class[]{});
        Object instance = localConstructor.newInstance(new Object[]{});
        mPluginActivity = (DLPlugin)instance;

        ((DLAttachable)mProxyActivity).attach(mPluginActivity, mPluginManager);
        mPluginActivity.attach(mProxyActivity, mPluginPackage);

        Bundle bundle = new Bundle();
        bundle.putInt(DLConstants.From, DLConstants.From_external);
        mPluginActivity.onCreate(bundle);
    }catch(Exception e){}
}

创建好了代理类对象和插件类对象,就需要通过两个attach()方法将它们绑定在一起。

第一个是代理类的attach()方法:

public void attach(DLPlugin remoteActivity, DLPluginManager pluginManager){
    mRemoteActivity = remoteActivity;
}

第二个是插件类的attach()方法:

public void attach(Activity proxyActivity, DLPluginPackage pluginPackage){
    mProxyActivity = proxyActivity;
    that = mProxyActivity;
    mPluginPackage = pluginPackage;
}

可以看到插件类里的that被赋值了代理类对象,所以在编写插件代码的时候this的功能将被废弃,不能使用this,必须使用that

Android DynamicLoadApk 代码剖析

代码结构:

一、internal包


1、DLAttachable接口:

public void attach(DLPlugin proxyActivity, DLPluginManager pluginManager)

代理启动插件的Activity,DL将会调用attach方法将代理Activity与插件管理器连接到插件Activity中,插件Activity将会加载插件的资源,所以代理Activity是插件内部的Activity的一个资源托管。

2、DLIntent:

Intent的子类,内部包含两个变量:1) 需要启动的插件的包名以,2) 需要启动的插件类名

3、DLPluginManager:

主要功能:获取Dex,启动服务,停止服务,绑定服务,解绑服务,启动Activity,加载指定class名的class,拷贝so文件等

START_RESULT_SUCCESS:插件启动成功
START_RESULT_NO_PKG  :没找到指定的包名的插件
START_RESULT_NO_CLASS:没找到需要启动的插件类
START_RESULT_TYPE_ERROR:

4、DLPluginPackage:

指定一个插件Apk。在相同的apk中的一系列的Activities共享的AssetManager、Resources以及DexClassLoader。

包含:
1)插件的完整包名
2)默认的Activity的名称
3)Dex类加载器
4)AssetManager对象
5)资源Resources对象
6)包的相关描述对象PackageInfo对象

5、DLProxyImpl:

是一个插件Activity的代理,代理将会利用反射创建插件Activity,并同时调用插件Activity的attach以及onCreate的方法,此时插件Activity就处于运行的状态了。

1)String mClass:当前插件Activity的类名
2)String mPackageName:当前插件Activity的包名
3)DLPluginPackage mPluginPackage:标识当前的插件Apk的对象
4)DLPluginManage mPluginManager:插件管理器
5)AssetManager mAssetManager:Asset管理器
6)Resources mResources:资源管理器
7)Theme mTheme:主题对象
8)ActivityInfo mActivityInfo:描述当前的插件Activity的相关的信息对象
9)Activity mProxyActivity:代理Activity对象
10)DLPlugin mPluginActivity:插件的接口
11)ClassLoader mPluginClassLoader:类加载器

6、DLServiceAttachable:

与DLAttachable的功能是类似的,只不过一个是针对Activity,一个是针对Service。

7、DLServiceProxyImpl:

1)利用类加载器加载指定的插件内部的服务名称,实际上是利用反射创建对象
2) 将插件内部的Service对象绑定到代理Service对象上
3)直接调用插件Service的onCreate的方法

二、dynamicload包


1、DLBasePluginActivity、DLBasePluginFragmentActivity

1、Activity mProxyActivity:Activity代理是真正运行中的Activity
2、Activity that:等同mProxyActivity
3、DLPluginManager mPluginManager:插件管理器
4、DLPluginPackage mPluginPackage:插件的包名

职责:
1、代理Activity的赋值
2、指向插件Apk对象的本身
3、类似onCreate、setContentView等Activity相关方法,判断是内部启动还是外部启动,如果是内部启动,完全可以不需要代理Activity的处理
4、凡是调用了onAttach,均是属于外部的启动

2、DLPlugin:

这是一个插件开发的接口:

public void onCreate(Bundle savedInstanceState);
public void onStart();
public void onRestart();
public void onActivityResult(int requestCode, int resultCode, Intent data);
public void onResume();
public void onPause();
public void onStop();
public void onDestroy();
public void attach(Activity proxyActivity, DLPluginPackage pluginPackage);
public void onSaveInstanceState(Bundle outState);
public void onNewIntent(Intent intent);
public void onRestoreInstanceState(Bundle savedInstanceState);
public boolean onTouchEvent(MotionEvent event);
public boolean onKeyUp(int keyCode, KeyEvent event);
public void onWindowAttributesChanged(LayoutParams params);
public void onWindowFocusChanged(boolean hasFocus);
public void onBackPressed();
public boolean onCreateOptionsMenu(Menu menu);
public boolean onOptionsItemSelected(MenuItem item);

3、DLProxyActivity、DLProxyFragmentActivity:

插件Activity的代理类。其中的主要成员变量:

1、DLPlugin:指向插件对象
2、DLProxyImpl:指向代理的实现对象

4、DLProxyService

Service代理服务

5、DLServicePlugin

一个接口,包括Service的关键性的方法。

三、util包


1、DLConfigs:

存储与获取so的修改时间

2、DLConstants:

在这个常量类中,主要配置下面几大类常量:

1、内部启动还是外部启动
2、Dex路径以及相关的包名,存储的Tag
3、Activity的相关类型
4、CPU相关的体系结构

3、DLUtils:

这个工具类包含以下功能:

1、获取在SDcard目录下指定插件Apk文件的包名getPackageInfo
2、获取在SDcard目录下指定插件Apk文件的应用图标getAppIcon
3、获取在SDcard目录下指定插件Apk文件的应用名称getAppLabel
4、利用反射机制获取指定的插件Activity中的类型

4、SoLibManager

1、获取CPU相关信息
2、在线程中将插件的so文件拷贝到当前工程目录的so文件的目录文件夹中(根据当前的cpu体系结构,拷贝合适的so文件)

Android Studio利用Gradle删除没有使用到的资源和代码文件

一、打包时忽略无用资源

  我们在打包的时候默认会把没有用到的资源(比如图片)也打包成app,徒增了应用的大小。现在我们可以利用Gradle来优雅的去除没有用到的资源文件了!

就是在gradle中配置shrinkResources true。这个东西依赖于minifyEnabled,所以minifyEnabled也要为true才行。

官方推荐在正式版中这么写:

android {
buildTypes {
release {
minifyEnabled true
shrinkResources true
}
}
}

如果你觉得debug包也太大,可以按照下面的方式写:

buildTypes {
debug {
minifyEnabled true
shrinkResources true
}
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}

我通过测试确实可以大大减少应用的大小,但也发现了一个问题,很多没有用到的类还是会被打包进去。比如你用了lib,lib里有很多很多类,你可能就用到了一个类,但却把lib中的所有类都打包了进去,很不合理。而且因为去除无效代码的功能要依赖于混淆,混淆又是一个耗时的操作,还是没有彻底解决打包慢的问题。

二、打包时忽略无用的代码
我目前的需求是很多工具类都在一个lib中,但我平常只用到lib中的几个类,所以希望生成apk的时候自动拍出lib中没有用到的类。于是我想到了下面的办法:

1.我首先在debug模式下配置一下,在debug时也进行混淆,并且自定义了混淆的配置文件debug-proguard-rules.pro

buildTypes {
debug {
minifyEnabled true // 是否混淆
shrinkResources true // 是否去除无效的资源文件
proguardFiles getDefaultProguardFile('proguard-android.txt'),'debug-proguard-rules.pro'
}
release {
minifyEnabled true // 是否混淆
shrinkResources true // 是否去除无效的资源文件
// 混淆的配置文件
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}

这样在debug的时候也会进行混淆,但我仅仅需要混淆lib包中的代码,而不混淆我app项目下的代码。这样就可以删除掉lib中无用的代码了。测试下来可以保证只有用到的东西才会被打包进来,其余的东西都没有被打包,效果不错!