Androidbug-android-sdk-macosx/cmake/3.6.4111459/bin/cmake

Build command failed.
Error while executing process /Users/xx/Documents/software/android-sdk-macosx/cmake/3.6.4111459/bin/cmake with arguments {-H/Users/xx/Documents/qtt/RTC_Android_v1.2.1/qtt_test/app -B/Users/xx/Documents/qtt/RTC_Android_v1.2.1/qtt_test/app/.externalNativeBuild/cmake/debug/armeabi -GAndroid Gradle - Ninja -DANDROID_ABI=armeabi -DANDROID_NDK=/Users/xx/Documents/software/android-sdk-macosx/ndk-bundle -DCMAKE_LIBRARY_OUTPUT_DIRECTORY=/Users/xx/Documents/qtt/RTC_Android_v1.2.1/qtt_test/app/build/intermediates/cmake/debug/obj/armeabi -DCMAKE_BUILD_TYPE=Debug -DCMAKE_MAKE_PROGRAM=/Users/xx/Documents/software/android-sdk-macosx/cmake/3.6.4111459/bin/ninja -DCMAKE_TOOLCHAIN_FILE=/Users/xx/Documents/software/android-sdk-macosx/ndk-bundle/build/cmake/android.toolchain.cmake -DANDROID_PLATFORM=android-16 -DCMAKE_CXX_FLAGS=}
(include) CMakeLists.txt
Open File
CMake Error: CMAKE_C_COMPILER not set, after EnableLanguage
CMake Error: CMAKE_CXX_COMPILER not set, after EnableLanguage
-- Configuring incomplete, errors occurred!
Build command failed.
Error while executing process /Users/xx/Documents/software/android-sdk-macosx/cmake/3.6.4111459/bin/cmake with arguments {-H/Users/xx/Documents/qtt/RTC_Android_v1.2.1/qtt_test/app -B/Users/xx/Documents/qtt/RTC_Android_v1.2.1/qtt_test/app/.externalNativeBuild/cmake/release/armeabi -GAndroid Gradle - Ninja -DANDROID_ABI=armeabi -DANDROID_NDK=/Users/xx/Documents/software/android-sdk-macosx/ndk-bundle -DCMAKE_LIBRARY_OUTPUT_DIRECTORY=/Users/xx/Documents/qtt/RTC_Android_v1.2.1/qtt_test/app/build/intermediates/cmake/release/obj/armeabi -DCMAKE_BUILD_TYPE=Release -DCMAKE_MAKE_PROGRAM=/Users/xx/Documents/software/android-sdk-macosx/cmake/3.6.4111459/bin/ninja -DCMAKE_TOOLCHAIN_FILE=/Users/xx/Documents/software/android-sdk-macosx/ndk-bundle/build/cmake/android.toolchain.cmake -DANDROID_PLATFORM=android-16 -DCMAKE_CXX_FLAGS=}
(include) CMakeLists.txt
Open File
CMake Error: CMAKE_C_COMPILER not set, after EnableLanguage
CMake Error: CMAKE_CXX_COMPILER not set, after EnableLanguage
-- Configuring incomplete, errors occurred!

却换NDK版本,我用了android-ndk-r15b

Glide

Transformation<Bitmap> circleCrop = new CircleCrop();
if (data.imgUrl.endsWith("gif")) {
Glide.with(context)
.asGif()
.diskCacheStrategy(DiskCacheStrategy.NONE)
.load(data.imgUrl)
.into(imageView);
} else if (data.imgUrl.endsWith("webp")) {
Glide.with(context)
.load(data.imgUrl)
.optionalTransform(circleCrop)
.optionalTransform(WebpDrawable.class, new WebpDrawableTransformation(circleCrop))
.into(imageView);
} else {
Glide.with(context)
.load(data.imgUrl)
.into(imageView);
}

文字阴影

<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