auto_create_wx_acitivty_by_annotation

如何使用注解生成WXEntryActivity?

在Android中使用注解时,需要创建java模块,否则无法导入javax.annotation.processing.AbstractProcessor;相关依赖。

1. android注解配置

1. 使用annotationProcessor设置参数

a) /gradle/wrapper/gradle-wrapper.properties

distributionUrl = https\://services.gradle.org/distributions/gradle-4.4-all.zip

b) 配置annotationProcessor

buildscript {
    repositories {
        // Gradle 4.1 and higher include support for Google's Maven repo using
        // the google() method. And you need to include this repo to download
        // Android plugin 3.0.0 or higher.
        google()
        ...
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.1.0'
    }
}

android {

  defaultConfig {

    javaCompileOptions {
    
      annotationProcessorOptions {
      
        arguments = [ 
        	key1 : 'key1Value', 
        	key2 : 'key2Value'
        ]
      }
    }
  }
}

dependencies {
    annotationProcessor 'xxx:xx:xxx'
}

2. 使用apt方式设置参数

buildscript {
    dependencies {
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
    }
}

apply plugin: 'com.neenbedankt.android-apt'

apt {
    arguments {
       key1 'key1Value'
       key2 'key2Value'
    }
}

dependencies {
    apt 'xxx:xx:xxx'
}

3. 在注解解析器中获取传入参数

processingEnvironment.getOptions()获取传递给注解的参数.

@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class AnnonationProcessor extends AbstractProcessor {
	@Override
	public synchronized void init(ProcessingEnvironment processingEnvironment) {
	    super.init(processingEnvironment);
	    Map<String, String> options = processingEnvironment.getOptions();
	}
}

4. 配置注解生效

  1. 在resources中添加该文件resources/META-INF/services/javax.annotation.processing.Processor,然后在文件内容中添加创建的注解处理器类,如下所示:

    $ cat resources/META-INF/services/javax.annotation.processing.Processor
    com.xxx.yyy.AnnonationProcessor
    

2. 使用注解自动创建WXEntryActivity

我们知道使用微信分享或者支付sdk时,微信会指定接收结果的回调Activity包名固定为${packageName}.wxapi,这样我们修改包名创建马甲包时就会比较麻烦,通过使用注解自动生成指定包名的activity可以很好的解决这个问题。

通过注解自动创建指定packageName的WXEntryActivity

1. 创建注解

在注解中指定包名,用于创建特定包名的WXEntryActivity。

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)

public @interface WXEntryActivityHandler {
    String packageName(); 
}

2. 创建注解处理器

  • 注解处理器

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.PrintWriter;
    import java.io.Writer;
    import java.util.Collections;
    import java.util.Set;
    
    import javax.annotation.processing.AbstractProcessor;
    import javax.annotation.processing.ProcessingEnvironment;
    import javax.annotation.processing.RoundEnvironment;
    import javax.annotation.processing.SupportedSourceVersion;
    import javax.lang.model.SourceVersion;
    import javax.lang.model.element.Element;
    import javax.lang.model.element.TypeElement;
    import javax.tools.Diagnostic;
    import javax.tools.JavaFileObject;
    
    @SupportedSourceVersion(SourceVersion.RELEASE_7)
    public class AnnonationProcessor extends AbstractProcessor {
    
    private static final String WX_ENTRY_ACTIVITY_TEMPLATE_PATH = "/WXEntryActivity.tmpl";
    
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        return Collections.singleton(WXEntryActivityHandler.class.getCanonicalName());
    }
    
    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
    }
    
    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        Set<? extends Element> elementsAnnotatedWith = roundEnvironment.getElementsAnnotatedWith(WXEntryActivityHandler.class);
        for (Element e : elementsAnnotatedWith) {
            generateShareHandlerCode(e);
        }
        return true;
    }
    
    private void generateShareHandlerCode(Element element) {
        if (!(element instanceof TypeElement)) {
            return;
        }
    
        WXEntryActivityHandler annotation = element.getAnnotation(WXEntryActivityHandler.class);
        if (annotation == null) {
            return;
        }
    
        String wxEntryActivityTmplText = readAll(WX_ENTRY_ACTIVITY_TEMPLATE_PATH);
        if (wxEntryActivityTmplText == null) {
            return;
        }
    
        TypeElement typeElement = (TypeElement) element;
        String wechatHandler = typeElement.getQualifiedName().toString();
        final String packageName = annotation.packageName();
        if (packageName == null || "".equals(packageName.trim())) {
            String msg = "WXEntryActivityHandler.packageName() for " + wechatHandler + " cannot be null or empty.";
            processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, msg);
            throw new RuntimeException(msg);
        }
    
        final String wxapiPackageName = packageName + ".wxapi";
        String generateCode = wxEntryActivityTmplText.replaceAll("%PACKAGE_NAME%", wxapiPackageName)
                .replaceAll("%WXEntryActivityCallbackImpl%", wechatHandler);
    
        try {
            JavaFileObject fileObject = processingEnv.getFiler().createSourceFile(wxapiPackageName + ".WXEntryActivity");
            processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Creating " + fileObject.toUri());
            Writer writer = fileObject.openWriter();
            try {
                PrintWriter pw = new PrintWriter(writer);
                pw.print(generateCode);
                pw.flush();
            } finally {
                writer.close();
            }
        } catch (IOException x) {
            processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, x.toString());
        }
    }
    
    private String readAll(String tmplPath) {
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(AnnonationProcessor.class.getResourceAsStream(tmplPath), "utf-8"));
            StringBuilder text = new StringBuilder();
            String line = null;
            while ((line = reader.readLine()) != null) {
                text.append(line);
            }
            reader.close();
    
            text.trimToSize();
            return text.toString();
        } catch (Throwable e) {
            processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "readAll from " + tmplPath + " error; " + e);
        }
    
        return null;
    }
    }
    
  • 模板代码

    package %PACKAGE_NAME%;
    
    import android.app.Activity;
    import android.content.Intent;
    import android.os.Bundle;
    
    import WXEntryActivityCallback;
    
    public class WXEntryActivity extends Activity {
    
    private WXEntryActivityCallback wxEntryActivityCallback = new %WXEntryActivityCallbackImpl%();
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        wxEntryActivityCallback.onCreate(this, savedInstanceState);
    }
    
    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        wxEntryActivityCallback.onNewIntent(this, intent);
    }
    
    @Override
    protected void onStart() {
        super.onStart();
        wxEntryActivityCallback.onStart();
    }
    
    @Override
    protected void onRestart() {
        super.onRestart();
        wxEntryActivityCallback.onRestart();
    }
    
    @Override
    protected void onResume() {
        super.onResume();
        wxEntryActivityCallback.onResume();
    }
    
    @Override
    protected void onPause() {
        super.onPause();
        wxEntryActivityCallback.onPause();
    }
    
    @Override
    protected void onStop() {
        super.onStop();
        wxEntryActivityCallback.onStop();
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        wxEntryActivityCallback.onDestroy();
    }
    }
    

3. 注解的使用

package com.xxx.yyy;

@WXEntryActivityHandler(packageName = BuildConfig.APPLICATION_ID)
public class WexinAPIEventHandler implements WXEntryActivityCallback, IWXAPIEventHandler {
    private static final String TAG = "WexinAPIEventHandler";

    private IWXAPI mWXApi;

    private Activity wxEntryActivity;

    public void onCreate(Activity wxEntryActivity, Bundle savedInstanceState) {
        this.wxEntryActivity = wxEntryActivity;
        mWXApi = WXAPIFactory.createWXAPI(wxEntryActivity, BuildConfig.WX_APP_ID);
        mWXApi.registerApp(BuildConfig.WX_APP_ID);
        try {
            mWXApi.handleIntent(wxEntryActivity.getIntent(), this);
        } catch (Throwable e) {
            Log.e(TAG, "onCreate error", e);
        }
    }

    public void onNewIntent(Activity wxEntryActivity, Intent intent) {
        try {
            mWXApi.handleIntent(intent, this);
        } catch (Throwable e) {
            Log.e(TAG, "onCreate onNewIntent", e);
        }
    }

    @Override
    public void onReq(BaseReq baseReq) {

        Log.e(TAG, " baseReq-type:" + baseReq.getType());
        WXMediaMessage wxMsg = ((ShowMessageFromWX.Req) baseReq).message;
        WXAppExtendObject obj = (WXAppExtendObject) wxMsg.mediaObject;
        StringBuffer msg = new StringBuffer(); // 组织一个待显示的消息内容
        msg.append("description: ");
        msg.append(wxMsg.description);
        msg.append("\n");
        msg.append("extInfo: ");
        msg.append(obj.extInfo);
        msg.append("\n");
        msg.append("filePath: ");
        msg.append(obj.filePath);
        Log.e(TAG, " baseReq-title:" + wxMsg.title + ":msg:" + msg.toString());

        switch (baseReq.getType()) {
            case ConstantsAPI.COMMAND_GETMESSAGE_FROM_WX:
                Log.e(TAG, " baseReq-title:1");
                break;
            case ConstantsAPI.COMMAND_SHOWMESSAGE_FROM_WX:
                Log.e(TAG, " baseReq-title:2");

                break;
            default:
                break;
        }
    }

    @Override
    public void onResp(BaseResp baseResp) {
        String result = "";
        Log.e(TAG, "baseresp.getType:" + baseResp.getType() + ":transaction:" + baseResp.transaction);

        switch (baseResp.errCode) {
            case BaseResp.ErrCode.ERR_OK:
                result = "发送成功";
                break;
            case BaseResp.ErrCode.ERR_USER_CANCEL:
                result = "发送取消";
                break;
            case BaseResp.ErrCode.ERR_AUTH_DENIED:
                result = "发送被拒绝";
                break;
            case BaseResp.ErrCode.ERR_UNSUPPORT:
                result = "不支持错误";
                break;
            default:
                result = "发送返回";
                break;
        }
        Log.e(TAG, "baseresp.getType-id:" + 
        Log.e(TAG, "baseresp.getType-result:" + result + "");
        this.wxEntryActivity.finish();
    }

    @Override
    public void onStart() {
        Log.e(TAG, "onStart");

    }

    @Override
    public void onRestart() {
        Log.e(TAG, "onRestart");

    }

    @Override
    public void onResume() {
        Log.e(TAG, "onResume");

    }

    @Override
    public void onPause() {
        Log.e(TAG, "onPause");

    }

    @Override
    public void onStop() {
        Log.e(TAG, "onStop");

    }

    @Override
    public void onDestroy() {
        Log.e(TAG, "onDestroy");

    }
}

4. 代码生成

通过gradle执行compileDebugJavaWithJavac,注解就会自动生成com.xxx.yyy.wxapi.WXEntryActivity类,最后在AndroidManifest中配置WXEntryActivity即可。

<activity
        android:name="${applicationId}.wxapi.WXEntryActivity"
        android:exported="true"
        android:screenOrientation="portrait" />