├── README.md ├── app ├── andfix.jks ├── build.gradle ├── libs │ ├── jpush-android-2.1.0.jar │ └── umeng-onlineconfig_v1.0.0.jar ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── zhw │ │ └── andfix │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── zhw │ │ │ └── andfix │ │ │ ├── BaseApplication.java │ │ │ ├── MainActivity.java │ │ │ ├── MyReceiver.java │ │ │ ├── TestActivity.java │ │ │ ├── consts │ │ │ └── SPConst.java │ │ │ ├── model │ │ │ └── PatchBean.java │ │ │ └── util │ │ │ ├── GsonUtils.java │ │ │ ├── LocalPreferencesHelper.java │ │ │ ├── RepairBugUtil.java │ │ │ └── WeakHandler.java │ ├── jniLibs │ │ ├── arm64-v8a │ │ │ ├── libandfix.so │ │ │ └── libjpush210.so │ │ ├── armeabi-v7a │ │ │ ├── libandfix.so │ │ │ └── libjpush210.so │ │ ├── armeabi │ │ │ └── libjpush210.so │ │ ├── mips │ │ │ ├── libandfix.so │ │ │ └── libjpush210.so │ │ ├── mips64 │ │ │ ├── libandfix.so │ │ │ └── libjpush210.so │ │ ├── x86 │ │ │ └── libjpush210.so │ │ └── x86_64 │ │ │ ├── libandfix.so │ │ │ └── libjpush210.so │ └── res │ │ ├── drawable-hdpi │ │ ├── border_bg.9.png │ │ ├── bottom_bg.9.png │ │ ├── corners_bg.xml │ │ ├── ic_launcher.png │ │ ├── ic_richpush_actionbar_back.png │ │ ├── ic_richpush_actionbar_divider.png │ │ ├── jpush_notification_icon.png │ │ ├── richpush_btn_selector.xml │ │ ├── stripes.png │ │ ├── tiledstripes.xml │ │ └── top_bg.9.png │ │ ├── drawable-ldpi │ │ └── ic_launcher.png │ │ ├── drawable-mdpi │ │ └── ic_launcher.png │ │ ├── drawable-xhdpi │ │ └── ic_launcher.png │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── activity_test.xml │ │ ├── customer_notitfication_layout.xml │ │ ├── customer_notitfication_layout_one.xml │ │ ├── jpush_popwin_layout.xml │ │ ├── jpush_webview_layout.xml │ │ ├── main.xml │ │ ├── push_set_dialog.xml │ │ └── set_push_time.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ ├── values-zh │ │ ├── strings.xml │ │ └── style.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── jpush_style.xml │ │ ├── strings.xml │ │ ├── style.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── zhw │ └── andfix │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties └── settings.gradle /README.md: -------------------------------------------------------------------------------- 1 | # AndFix-Bad-Practices 2 | AndFix应用到项目的一个小栗子。个人技术水平只能写写这些没有技术的Demo了,请大家不要吐槽~ 3 | 4 | # How To Work 5 | 1. 应用启动的时候,在 onCreate() 方法中获取友盟的在线参数来判断当前的应用版本是否有补丁需要下载,有则通过ThinDonloadManager来下载到SD下并且通过使用AndFix来加载到应用中。 6 | 2. 使用极光推送消息到该应用的版本需要下载补丁,如果应用收到了消息后,应用判断当前的版本是否需要下载补丁。如果应用没有收到消息的通知,则下次启动App的时候,获取友盟在线参数来判断是否需要下载补丁。 7 | 8 | # Part Code 9 | 10 | 通过ThinDownloadManager下载补丁包,下载成功后使用AndFix加载补丁包的方法: 11 | 12 | public void downloadAndLoad(Context context, final PatchBean bean, String downloadUrl) { 13 | if (mLocalPreferencesHelper == null) { 14 | mLocalPreferencesHelper = new LocalPreferencesHelper(context, SPConst.SP_NAME); 15 | } 16 | Uri downloadUri = Uri.parse(downloadUrl); 17 | Uri destinationUri = Uri.parse(Environment.getExternalStorageDirectory() 18 | .getAbsolutePath() + bean.url); 19 | DownloadRequest downloadRequest = new DownloadRequest(downloadUri) 20 | .setDestinationURI(destinationUri) 21 | .setPriority(DownloadRequest.Priority.HIGH) 22 | .setDownloadListener(new DownloadStatusListener() { 23 | @Override 24 | public void onDownloadComplete(int id) { 25 | // add patch at runtime 26 | try { 27 | // .apatch file path 28 | String patchFileString = Environment.getExternalStorageDirectory() 29 | .getAbsolutePath() + bean.url; 30 | BaseApplication.mPatchManager.addPatch(patchFileString); 31 | Log.d(TAG, "apatch:" + patchFileString + " added."); 32 | 33 | //复制且加载补丁成功后,删除下载的补丁 34 | File f = new File(patchFileString); 35 | if (f.exists()) { 36 | boolean result = new File(patchFileString).delete(); 37 | if (!result) 38 | Log.e(TAG, patchFileString + " delete fail"); 39 | } 40 | // mLocalPreferencesHelper.saveOrUpdate(SPConst.IsHavePathDownLoad, false); 41 | } catch (IOException e) { 42 | Log.e(TAG, "", e); 43 | } catch (Throwable throwable) { 44 | 45 | } 46 | } 47 | 48 | @Override 49 | public void onDownloadFailed(int id, int errorCode, String errorMessage) { 50 | //下载失败的时候,标注标记位,等下次重新打开应用的时候重新下载 51 | // mLocalPreferencesHelper.saveOrUpdate(SPConst.IsHavePathDownLoad, true); 52 | Log.e(TAG, "onDownloadFailed"); 53 | 54 | } 55 | 56 | @Override 57 | public void onProgress(int id, long totalBytes, int progress) { 58 | Log.e(TAG, "progress:" + progress); 59 | } 60 | }); 61 | mDownloadManager = new ThinDownloadManager(THREAD_COUNT); 62 | mDownloadManager.add(downloadRequest); 63 | } 64 | 65 | 判断是否有补丁包需要下载的方法: 66 | 67 | public void comparePath(Context context, PatchBean RemoteBean) throws Exception { 68 | String pathInfo = mLocalPreferencesHelper.getString(SPConst.PATH_INFO); 69 | final PatchBean localBean = GsonUtils.getInstance().parseIfNull(PatchBean.class, pathInfo); 70 | //远程的应用版本跟当前应用的版本比较 71 | if (BaseApplication.VERSION_NAME.equals(RemoteBean.app_v)) { 72 | //远程的应用版本跟本地保存的应用版本一样,但补丁不一样,则需要下载重新 73 | /** 74 | *第一种情况:当本地记录的Bean为空的时候(刚安装的时候可能为空)并且远程的Bean的path_v不为空的时候需要下载补丁。 75 | * 第二种情况:当本地记录的path_v和远程Bean的path_v不一样的时候需要下载补丁。 76 | */ 77 | if (localBean == null && !TextUtils.isEmpty(RemoteBean.path_v) 78 | || localBean.app_v.equals(RemoteBean.app_v) && 79 | !localBean.path_v.equals(RemoteBean.path_v)) { 80 | downloadAndLoad(context, RemoteBean, 81 | SPConst.URL_PREFIX + RemoteBean.url); 82 | String json = GsonUtils.getInstance().parse(RemoteBean); 83 | mLocalPreferencesHelper.saveOrUpdate(SPConst.PATH_INFO, json); 84 | } /*else { 85 | mLocalPreferencesHelper.saveOrUpdate(SPConst.IsHavePathDownLoad, false); 86 | }*/ 87 | } 88 | } 89 | 90 | # Platform Config 91 | * 友盟在线参数 92 | ![](http://7xrnko.com1.z0.glb.clouddn.com/umneg_online.png) 93 | 94 | * 极光推送自定义消息 95 | ![](http://7xrnko.com1.z0.glb.clouddn.com/jpush.png) 96 | 97 | 98 | # Note 99 | - 如果你在使用AndFix的时候遇到关于so问题的报错,可以看一下这个的,希望它能帮助你解决问题。 100 | [https://github.com/zhonghanwen/AndFix-Ndk-Build-ADT](https://github.com/zhonghanwen/AndFix-Ndk-Build-ADT) 101 | 102 | # Thanks 103 | * [AndFix](https://github.com/alibaba/AndFix) 104 | * [ThinDownloadManager](https://github.com/smanikandan14/ThinDownloadManager) 105 | * JPush 106 | * Umeng 107 | 108 | # License 109 | 110 | Copyright 2016 zhonghanwen 111 | 112 | Licensed under the Apache License, Version 2.0 (the "License"); 113 | you may not use this file except in compliance with the License. 114 | You may obtain a copy of the License at 115 | 116 | http://www.apache.org/licenses/LICENSE-2.0 117 | 118 | Unless required by applicable law or agreed to in writing, software 119 | distributed under the License is distributed on an "AS IS" BASIS, 120 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 121 | See the License for the specific language governing permissions and 122 | limitations under the License. -------------------------------------------------------------------------------- /app/andfix.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhonghanwen/AndFix-Bad-Practices/5471ea037fe0512608e93ff61a632c32e6bf9986/app/andfix.jks -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.1" 6 | 7 | defaultConfig { 8 | applicationId "com.zhw.andfix" 9 | minSdkVersion 15 10 | targetSdkVersion 23 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | 15 | //签名 16 | signingConfigs { 17 | debug { 18 | storeFile file(ANDROID_STORE_FILE) 19 | storePassword ANDROID_STORE_PASS 20 | keyAlias ANDROID_KEY_ALIAS 21 | keyPassword ANDROID_KEY_PASS 22 | } 23 | 24 | release { 25 | storeFile file(ANDROID_STORE_FILE) 26 | storePassword ANDROID_STORE_PASS 27 | keyAlias ANDROID_KEY_ALIAS 28 | keyPassword ANDROID_KEY_PASS 29 | } 30 | } 31 | 32 | buildTypes { 33 | release { 34 | minifyEnabled false 35 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 36 | signingConfig signingConfigs.debug 37 | } 38 | } 39 | } 40 | 41 | dependencies { 42 | compile fileTree(include: ['*.jar'], dir: 'libs') 43 | testCompile 'junit:junit:4.12' 44 | compile 'com.android.support:appcompat-v7:23.2.0' 45 | compile 'com.android.support:support-v4:23.0.1' 46 | //下载器相关 47 | compile 'com.mani:thindownloadmanager:1.0.0' 48 | compile 'com.alipay.euler:andfix:0.3.1@aar' 49 | compile 'com.google.code.gson:gson:+' 50 | compile files('libs/jpush-android-2.1.0.jar') 51 | compile files('libs/umeng-onlineconfig_v1.0.0.jar') 52 | } 53 | -------------------------------------------------------------------------------- /app/libs/jpush-android-2.1.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhonghanwen/AndFix-Bad-Practices/5471ea037fe0512608e93ff61a632c32e6bf9986/app/libs/jpush-android-2.1.0.jar -------------------------------------------------------------------------------- /app/libs/umeng-onlineconfig_v1.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhonghanwen/AndFix-Bad-Practices/5471ea037fe0512608e93ff61a632c32e6bf9986/app/libs/umeng-onlineconfig_v1.0.0.jar -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in C:\Program Files\Android\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/zhw/andfix/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.zhw.andfix; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 39 | 40 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 59 | 60 | 61 | 62 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 78 | 79 | 80 | 81 | 82 | 83 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 143 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /app/src/main/java/com/zhw/andfix/BaseApplication.java: -------------------------------------------------------------------------------- 1 | package com.zhw.andfix; 2 | 3 | import android.app.Application; 4 | import android.content.pm.PackageInfo; 5 | import android.content.pm.PackageManager; 6 | import android.util.Log; 7 | 8 | import com.alipay.euler.andfix.patch.PatchManager; 9 | 10 | import cn.jpush.android.api.JPushInterface; 11 | 12 | /** 13 | * Created by zhonghw on 2016/3/10. 14 | */ 15 | public class BaseApplication extends Application { 16 | 17 | private static final String TAG = "AndFix"; 18 | public static String VERSION_NAME = ""; 19 | public static PatchManager mPatchManager; 20 | 21 | @Override 22 | public void onCreate() { 23 | super.onCreate(); 24 | try { 25 | PackageInfo mPackageInfo = this.getPackageManager().getPackageInfo( 26 | this.getPackageName(), 0); 27 | VERSION_NAME = mPackageInfo.versionName; 28 | } catch (PackageManager.NameNotFoundException e) { 29 | e.printStackTrace(); 30 | } 31 | JPushInterface.init(this); 32 | initAndFix(); 33 | } 34 | 35 | 36 | private void initAndFix() { 37 | // initialize 38 | 39 | mPatchManager = new PatchManager(this); 40 | mPatchManager.init(VERSION_NAME); 41 | Log.d(TAG, "inited."); 42 | 43 | // load patch 44 | mPatchManager.loadPatch(); 45 | // Log.d(TAG, "apatch loaded."); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/java/com/zhw/andfix/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.zhw.andfix; 2 | 3 | import android.app.Activity; 4 | import android.content.BroadcastReceiver; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.content.IntentFilter; 8 | import android.os.Bundle; 9 | import android.os.Handler; 10 | import android.os.Message; 11 | import android.text.TextUtils; 12 | import android.view.View; 13 | 14 | import com.umeng.onlineconfig.OnlineConfigAgent; 15 | import com.zhw.andfix.model.PatchBean; 16 | import com.zhw.andfix.util.GsonUtils; 17 | import com.zhw.andfix.util.RepairBugUtil; 18 | import com.zhw.andfix.util.WeakHandler; 19 | 20 | 21 | public class MainActivity extends Activity { 22 | 23 | public static final int MSG_WHAT_DOWNLOAD = 0x111; 24 | public static final String UMENG_ONLINE_PARAM = "pathInfo"; //友盟后台定义的参数名称 25 | 26 | 27 | @Override 28 | protected void onCreate(Bundle savedInstanceState) { 29 | super.onCreate(savedInstanceState); 30 | setContentView(R.layout.activity_main); 31 | registerMessageReceiver(); // used for receive msg 32 | initUmeng(); //初始化友盟在线参数 33 | /*boolean haveDownload = mLocalPreferencesHelper.getBooleanDefaultFalse( 34 | getString(R.string.spp_app_have_download)); 35 | if (haveDownload){ 36 | RepairBugUtil.getInstance().fixBug(this); 37 | }*/ 38 | 39 | getUmengParamAndFix(); 40 | 41 | } 42 | 43 | private void getUmengParamAndFix() { 44 | //获取友盟在线参数对应key的values 45 | String pathInfo = OnlineConfigAgent.getInstance().getConfigParams(this, UMENG_ONLINE_PARAM); 46 | if (!TextUtils.isEmpty(pathInfo)){ 47 | PatchBean onLineBean = GsonUtils.getInstance().parseIfNull(PatchBean.class , pathInfo); 48 | try { 49 | //进行判断当前版本是否有补丁需要下载更新 50 | RepairBugUtil.getInstance().comparePath(this, onLineBean); 51 | } catch (Exception e) { 52 | e.printStackTrace(); 53 | } 54 | } 55 | } 56 | 57 | /** 58 | * 友盟在线的初始化工作 59 | */ 60 | private void initUmeng() { 61 | OnlineConfigAgent.getInstance().updateOnlineConfig(this); 62 | OnlineConfigAgent.getInstance().setDebugMode(true); //设置在线参数的调试模式 63 | } 64 | 65 | 66 | public void onClick(View view) { 67 | TestActivity.launch(this); 68 | } 69 | 70 | 71 | @Override 72 | protected void onDestroy() { 73 | unregisterReceiver(mMessageReceiver); 74 | RepairBugUtil.getInstance().release(); 75 | super.onDestroy(); 76 | } 77 | 78 | 79 | 80 | private WeakHandler mHandler = new WeakHandler(new Handler.Callback() { 81 | @Override 82 | public boolean handleMessage(Message msg) { 83 | if (msg.what == MSG_WHAT_DOWNLOAD){ 84 | String message = (String) msg.obj; 85 | if (TextUtils.isEmpty(message)) return false; 86 | try { 87 | PatchBean bean = GsonUtils.getInstance().parse(PatchBean.class, message); 88 | RepairBugUtil.getInstance().comparePath(MainActivity.this, bean); 89 | } catch (Exception e) { 90 | e.printStackTrace(); 91 | } 92 | } 93 | return false; 94 | } 95 | }); 96 | 97 | 98 | 99 | //for receive customer msg from jpush server 100 | private MessageReceiver mMessageReceiver; 101 | public static final String MESSAGE_RECEIVED_ACTION = "com.zhw.andfix.MESSAGE_RECEIVED_ACTION"; 102 | public static final String KEY_MESSAGE = "message"; 103 | 104 | public void registerMessageReceiver() { 105 | mMessageReceiver = new MessageReceiver(); 106 | IntentFilter filter = new IntentFilter(); 107 | filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); 108 | filter.addAction(MESSAGE_RECEIVED_ACTION); 109 | registerReceiver(mMessageReceiver, filter); 110 | } 111 | 112 | public class MessageReceiver extends BroadcastReceiver { 113 | 114 | @Override 115 | public void onReceive(Context context, Intent intent) { 116 | if (MESSAGE_RECEIVED_ACTION.equals(intent.getAction())) { 117 | String message = intent.getStringExtra(KEY_MESSAGE); 118 | Message msg = new Message(); 119 | msg.what = MSG_WHAT_DOWNLOAD; 120 | msg.obj = message; 121 | mHandler.sendMessage(msg); 122 | } 123 | } 124 | } 125 | 126 | 127 | } 128 | -------------------------------------------------------------------------------- /app/src/main/java/com/zhw/andfix/MyReceiver.java: -------------------------------------------------------------------------------- 1 | package com.zhw.andfix; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.os.Bundle; 7 | import android.speech.tts.TextToSpeech; 8 | import android.text.TextUtils; 9 | import android.util.Log; 10 | 11 | import com.google.gson.reflect.TypeToken; 12 | import com.zhw.andfix.consts.SPConst; 13 | import com.zhw.andfix.model.PatchBean; 14 | import com.zhw.andfix.util.GsonUtils; 15 | import com.zhw.andfix.util.LocalPreferencesHelper; 16 | import com.zhw.andfix.util.RepairBugUtil; 17 | 18 | import org.json.JSONException; 19 | import org.json.JSONObject; 20 | 21 | import java.util.Iterator; 22 | 23 | import cn.jpush.android.api.JPushInterface; 24 | 25 | /** 26 | * 自定义接收器 27 | * 28 | * 如果不定义这个 Receiver,则: 29 | * 1) 默认用户会打开主界面 30 | * 2) 接收不到自定义消息 31 | */ 32 | public class MyReceiver extends BroadcastReceiver { 33 | private static final String TAG = "JPush"; 34 | private LocalPreferencesHelper mLocalPreferencesHelper; 35 | 36 | @Override 37 | public void onReceive(Context context, Intent intent) { 38 | Bundle bundle = intent.getExtras(); 39 | Log.d(TAG, "[MyReceiver] onReceive - " + intent.getAction() + ", extras: " + printBundle(bundle)); 40 | 41 | if (JPushInterface.ACTION_REGISTRATION_ID.equals(intent.getAction())) { 42 | String regId = bundle.getString(JPushInterface.EXTRA_REGISTRATION_ID); 43 | Log.d(TAG, "[MyReceiver] 接收Registration Id : " + regId); 44 | //send the Registration Id to your server... 45 | 46 | } else if (JPushInterface.ACTION_MESSAGE_RECEIVED.equals(intent.getAction())) { 47 | Log.d(TAG, "[MyReceiver] 接收到推送下来的自定义消息: " + bundle.getString(JPushInterface.EXTRA_MESSAGE)); 48 | processCustomMessage(context, bundle); 49 | 50 | } else if (JPushInterface.ACTION_NOTIFICATION_RECEIVED.equals(intent.getAction())) { 51 | Log.d(TAG, "[MyReceiver] 接收到推送下来的通知"); 52 | int notifactionId = bundle.getInt(JPushInterface.EXTRA_NOTIFICATION_ID); 53 | Log.d(TAG, "[MyReceiver] 接收到推送下来的通知的ID: " + notifactionId); 54 | 55 | } else if (JPushInterface.ACTION_NOTIFICATION_OPENED.equals(intent.getAction())) { 56 | /* Log.d(TAG, "[MyReceiver] 用户点击打开了通知"); 57 | 58 | //打开自定义的Activity 59 | Intent i = new Intent(context, TestActivity.class); 60 | i.putExtras(bundle); 61 | //i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 62 | i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP ); 63 | context.startActivity(i);*/ 64 | 65 | } else if (JPushInterface.ACTION_RICHPUSH_CALLBACK.equals(intent.getAction())) { 66 | Log.d(TAG, "[MyReceiver] 用户收到到RICH PUSH CALLBACK: " + bundle.getString(JPushInterface.EXTRA_EXTRA)); 67 | //在这里根据 JPushInterface.EXTRA_EXTRA 的内容处理代码,比如打开新的Activity, 打开一个网页等.. 68 | 69 | } else if(JPushInterface.ACTION_CONNECTION_CHANGE.equals(intent.getAction())) { 70 | boolean connected = intent.getBooleanExtra(JPushInterface.EXTRA_CONNECTION_CHANGE, false); 71 | Log.w(TAG, "[MyReceiver]" + intent.getAction() +" connected state change to "+connected); 72 | } else { 73 | Log.d(TAG, "[MyReceiver] Unhandled intent - " + intent.getAction()); 74 | } 75 | } 76 | 77 | // 打印所有的 intent extra 数据 78 | private static String printBundle(Bundle bundle) { 79 | StringBuilder sb = new StringBuilder(); 80 | for (String key : bundle.keySet()) { 81 | if (key.equals(JPushInterface.EXTRA_NOTIFICATION_ID)) { 82 | sb.append("\nkey:" + key + ", value:" + bundle.getInt(key)); 83 | }else if(key.equals(JPushInterface.EXTRA_CONNECTION_CHANGE)){ 84 | sb.append("\nkey:" + key + ", value:" + bundle.getBoolean(key)); 85 | } else if (key.equals(JPushInterface.EXTRA_EXTRA)) { 86 | if (bundle.getString(JPushInterface.EXTRA_EXTRA).isEmpty()) { 87 | Log.i(TAG, "This message has no Extra data"); 88 | continue; 89 | } 90 | 91 | try { 92 | JSONObject json = new JSONObject(bundle.getString(JPushInterface.EXTRA_EXTRA)); 93 | Iterator it = json.keys(); 94 | 95 | while (it.hasNext()) { 96 | String myKey = it.next().toString(); 97 | sb.append("\nkey:" + key + ", value: [" + 98 | myKey + " - " +json.optString(myKey) + "]"); 99 | } 100 | } catch (JSONException e) { 101 | Log.e(TAG, "Get message extra JSON error!"); 102 | } 103 | 104 | } else { 105 | sb.append("\nkey:" + key + ", value:" + bundle.getString(key)); 106 | } 107 | } 108 | return sb.toString(); 109 | } 110 | 111 | //send msg to MainActivity 112 | /* 113 | private void processCustomMessage(Context context, Bundle bundle) { 114 | if (mLocalPreferencesHelper == null) { 115 | mLocalPreferencesHelper = new LocalPreferencesHelper(context, SPConst.SP_NAME); 116 | } 117 | String pathInfo = mLocalPreferencesHelper.getString(SPConst.PATH_INFO); 118 | final PatchBean localBean = GsonUtils.getInstance().parseIfNull(PatchBean.class , pathInfo); 119 | String message = bundle.getString(JPushInterface.EXTRA_MESSAGE); 120 | if (TextUtils.isEmpty(message)) return; 121 | try { 122 | PatchBean bean = GsonUtils.getInstance().parse(PatchBean.class, message); 123 | if (BaseApplication.VERSION_NAME.equals(bean.app_v)){ //远程的应用版本跟当前应用的版本比较 124 | if (localBean == null && !TextUtils.isEmpty(bean.path_v) 125 | || localBean.app_v.equals(bean.app_v) && 126 | ! localBean.path_v.equals(bean.path_v)){ //远程的应用版本跟本地保存的应用版本一样,但补丁不一样,则需要下载重新 127 | RepairBugUtil.getInstance().downloadAndLoad(context, bean, SPConst.URL_PREFIX + bean.url); 128 | String json = GsonUtils.getInstance().parse(bean); 129 | mLocalPreferencesHelper.saveOrUpdate(SPConst.PATH_INFO, json); 130 | }else { 131 | mLocalPreferencesHelper.saveOrUpdate(SPConst.IsHavePathDownLoad, false); 132 | } 133 | } 134 | } catch (Exception e) { 135 | e.printStackTrace(); 136 | } 137 | Log.e(TAG,message); 138 | } 139 | */ 140 | 141 | 142 | private void processCustomMessage(Context context, Bundle bundle) { 143 | String message = bundle.getString(JPushInterface.EXTRA_MESSAGE); 144 | Intent msgIntent = new Intent(MainActivity.MESSAGE_RECEIVED_ACTION); 145 | msgIntent.putExtra(MainActivity.KEY_MESSAGE, message); 146 | context.sendBroadcast(msgIntent); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /app/src/main/java/com/zhw/andfix/TestActivity.java: -------------------------------------------------------------------------------- 1 | package com.zhw.andfix; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.widget.Toast; 7 | 8 | import com.zhw.andfix.model.PatchBean; 9 | 10 | /** 11 | * Created by zhonghw on 2016/3/11. 12 | */ 13 | public class TestActivity extends Activity { 14 | 15 | PatchBean bean; 16 | 17 | public static void launch(Activity activity){ 18 | Intent intent = new Intent(activity, TestActivity.class); 19 | activity.startActivity(intent); 20 | } 21 | 22 | @Override 23 | protected void onCreate(Bundle savedInstanceState) { 24 | super.onCreate(savedInstanceState); 25 | setContentView(R.layout.activity_test); 26 | /*bean = new PatchBean(); 27 | bean.url = "repair the bug";*/ 28 | /*tetBug();*/ 29 | 30 | } 31 | 32 | private void tetBug() { 33 | Toast.makeText(this, "hello,world!!!", Toast.LENGTH_SHORT).show(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/zhw/andfix/consts/SPConst.java: -------------------------------------------------------------------------------- 1 | package com.zhw.andfix.consts; 2 | 3 | /** 4 | * Created by zhonghw on 2016/3/11. 5 | */ 6 | public class SPConst { 7 | public static final String SP_NAME = "app"; 8 | public static final String IsHavePathDownLoad = "IsHavePathDownLoad"; 9 | public static final String PATH_INFO = "pathInfo"; 10 | public static final String URL_PREFIX = "http://7xrnko.com1.z0.glb.clouddn.com"; 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/zhw/andfix/model/PatchBean.java: -------------------------------------------------------------------------------- 1 | package com.zhw.andfix.model; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * Created by zhonghw on 2016/3/11. 7 | */ 8 | public class PatchBean implements Serializable { 9 | public String app_v; //应用的版本 10 | public String path_v; //补丁的版本 11 | public String url; //补丁的下载地址 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/java/com/zhw/andfix/util/GsonUtils.java: -------------------------------------------------------------------------------- 1 | package com.zhw.andfix.util; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | import com.google.gson.JsonSyntaxException; 6 | 7 | import java.io.InputStream; 8 | import java.io.InputStreamReader; 9 | import java.io.Reader; 10 | import java.lang.reflect.Type; 11 | 12 | public class GsonUtils { 13 | 14 | private Gson mGson; 15 | 16 | private GsonUtils() { 17 | mGson = new GsonBuilder() 18 | .serializeNulls().setPrettyPrinting() // 对json结果格式化. 19 | .create(); 20 | } 21 | 22 | private static class SingletonHolder { 23 | public static final GsonUtils INSTANCE = new GsonUtils(); 24 | } 25 | 26 | public static GsonUtils getInstance() { 27 | return SingletonHolder.INSTANCE; 28 | } 29 | 30 | public Gson getGson() { 31 | return mGson; 32 | } 33 | 34 | public T parseIfNull(Type typeOfT, InputStream is) { 35 | try { 36 | return mGson.fromJson(new InputStreamReader(is), typeOfT); 37 | } catch (JsonSyntaxException e) { 38 | e.printStackTrace(); 39 | return null; 40 | } 41 | } 42 | 43 | public T parseIfNull(Type typeOfT, String json) { 44 | try { 45 | return mGson.fromJson(json, typeOfT); 46 | } catch (JsonSyntaxException e) { 47 | e.printStackTrace(); 48 | return null; 49 | } 50 | } 51 | 52 | public T parse(Type typeOfT, String json) throws Exception { 53 | try { 54 | return mGson.fromJson(json, typeOfT); 55 | } catch (JsonSyntaxException e) { 56 | e.printStackTrace(); 57 | throw new Exception(e); 58 | } 59 | } 60 | 61 | public T parseIfNull(Type typeOfT, Reader json) { 62 | try { 63 | return mGson.fromJson(json, typeOfT); 64 | } catch (JsonSyntaxException e) { 65 | e.printStackTrace(); 66 | return null; 67 | } 68 | } 69 | 70 | public T parse(Type typeOfT, Reader json) throws Exception { 71 | try { 72 | return mGson.fromJson(json, typeOfT); 73 | } catch (JsonSyntaxException e) { 74 | e.printStackTrace(); 75 | throw new Exception(e); 76 | } 77 | } 78 | 79 | public T parseIfNull(Class clazz, String json) { 80 | try { 81 | return mGson.fromJson(json, clazz); 82 | } catch (JsonSyntaxException e) { 83 | e.printStackTrace(); 84 | return null; 85 | } 86 | } 87 | 88 | public T parse(Class clazz, String json) throws Exception { 89 | try { 90 | return mGson.fromJson(json, clazz); 91 | } catch (JsonSyntaxException e) { 92 | e.printStackTrace(); 93 | throw new Exception(e); 94 | } 95 | } 96 | 97 | public String parseIfNull(T object) { 98 | try { 99 | return mGson.toJson(object); 100 | } catch (JsonSyntaxException e) { 101 | e.printStackTrace(); 102 | return null; 103 | } 104 | } 105 | 106 | public String parse(T object) throws Exception { 107 | try { 108 | return mGson.toJson(object); 109 | } catch (JsonSyntaxException e) { 110 | e.printStackTrace(); 111 | throw new Exception(e); 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /app/src/main/java/com/zhw/andfix/util/LocalPreferencesHelper.java: -------------------------------------------------------------------------------- 1 | package com.zhw.andfix.util; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | 6 | public class LocalPreferencesHelper { 7 | private SharedPreferences mSharedPreferences; 8 | 9 | public LocalPreferencesHelper(Context context, String dbName) { 10 | mSharedPreferences = context.getSharedPreferences(dbName, 11 | Context.MODE_PRIVATE); 12 | } 13 | 14 | public void saveOrUpdate(String key, String value) { 15 | mSharedPreferences.edit().putString(key, value).commit(); 16 | } 17 | 18 | public void saveOrUpdate(String key, int value) { 19 | mSharedPreferences.edit().putInt(key, value).commit(); 20 | } 21 | 22 | public void saveOrUpdate(String key, float value) { 23 | mSharedPreferences.edit().putFloat(key, value).commit(); 24 | } 25 | 26 | public void saveOrUpdate(String key, boolean value) { 27 | mSharedPreferences.edit().putBoolean(key, value).commit(); 28 | } 29 | 30 | public void saveOrUpdate(String key, long value) { 31 | mSharedPreferences.edit().putLong(key, value).commit(); 32 | } 33 | 34 | public void del(String key) { 35 | mSharedPreferences.edit().remove(key).commit(); 36 | } 37 | 38 | public float getFloat(String key) { 39 | return mSharedPreferences.getFloat(key, -1); 40 | } 41 | 42 | public String getString(String key) { 43 | return mSharedPreferences.getString(key, ""); 44 | } 45 | 46 | public boolean getBooleanDefaultFalse(String key) { 47 | return mSharedPreferences.getBoolean(key, false); 48 | } 49 | 50 | public boolean getBooleanDefaultTrue(String key) { 51 | return mSharedPreferences.getBoolean(key, true); 52 | } 53 | 54 | public int getInt(String key) { 55 | return mSharedPreferences.getInt(key, -1); 56 | } 57 | 58 | public int getInt(String key, int defValue) { 59 | return mSharedPreferences.getInt(key, defValue); 60 | } 61 | 62 | public long getLong(String key) { 63 | return mSharedPreferences.getLong(key, -1); 64 | } 65 | 66 | public long getLong(String key, long defValue) { 67 | return mSharedPreferences.getLong(key, defValue); 68 | } 69 | } -------------------------------------------------------------------------------- /app/src/main/java/com/zhw/andfix/util/RepairBugUtil.java: -------------------------------------------------------------------------------- 1 | package com.zhw.andfix.util; 2 | 3 | import android.content.Context; 4 | import android.net.Uri; 5 | import android.os.Environment; 6 | import android.text.TextUtils; 7 | import android.util.Log; 8 | 9 | import com.google.gson.reflect.TypeToken; 10 | import com.thin.downloadmanager.DownloadRequest; 11 | import com.thin.downloadmanager.DownloadStatusListener; 12 | import com.thin.downloadmanager.ThinDownloadManager; 13 | import com.zhw.andfix.BaseApplication; 14 | import com.zhw.andfix.R; 15 | import com.zhw.andfix.consts.SPConst; 16 | import com.zhw.andfix.model.PatchBean; 17 | 18 | import java.io.File; 19 | import java.io.IOException; 20 | 21 | /** 22 | * Created by zhonghw on 2016/3/11. 23 | */ 24 | public class RepairBugUtil { 25 | 26 | private static final String TAG = "AndFix"; 27 | private static final int THREAD_COUNT = 3; //下载的线程数 28 | private ThinDownloadManager mDownloadManager; 29 | private LocalPreferencesHelper mLocalPreferencesHelper; 30 | 31 | private static class SingletonHolder { 32 | public static final RepairBugUtil INSTANCE = new RepairBugUtil(); 33 | } 34 | 35 | public static RepairBugUtil getInstance() { 36 | return SingletonHolder.INSTANCE; 37 | } 38 | 39 | public void downloadAndLoad(Context context, final PatchBean bean, String downloadUrl) { 40 | if (mLocalPreferencesHelper == null) { 41 | mLocalPreferencesHelper = new LocalPreferencesHelper(context, SPConst.SP_NAME); 42 | } 43 | Uri downloadUri = Uri.parse(downloadUrl); 44 | Uri destinationUri = Uri.parse(Environment.getExternalStorageDirectory() 45 | .getAbsolutePath() + bean.url); 46 | DownloadRequest downloadRequest = new DownloadRequest(downloadUri) 47 | .setDestinationURI(destinationUri) 48 | .setPriority(DownloadRequest.Priority.HIGH) 49 | .setDownloadListener(new DownloadStatusListener() { 50 | @Override 51 | public void onDownloadComplete(int id) { 52 | // add patch at runtime 53 | try { 54 | // .apatch file path 55 | String patchFileString = Environment.getExternalStorageDirectory() 56 | .getAbsolutePath() + bean.url; 57 | BaseApplication.mPatchManager.addPatch(patchFileString); 58 | Log.d(TAG, "apatch:" + patchFileString + " added."); 59 | 60 | //复制且加载补丁成功后,删除下载的补丁 61 | File f = new File(patchFileString); 62 | if (f.exists()) { 63 | boolean result = new File(patchFileString).delete(); 64 | if (!result) 65 | Log.e(TAG, patchFileString + " delete fail"); 66 | } 67 | // mLocalPreferencesHelper.saveOrUpdate(SPConst.IsHavePathDownLoad, false); 68 | } catch (IOException e) { 69 | Log.e(TAG, "", e); 70 | } catch (Throwable throwable) { 71 | 72 | } 73 | } 74 | 75 | @Override 76 | public void onDownloadFailed(int id, int errorCode, String errorMessage) { 77 | //下载失败的时候,标注标记位,等下次重新打开应用的时候重新下载 78 | // mLocalPreferencesHelper.saveOrUpdate(SPConst.IsHavePathDownLoad, true); 79 | Log.e(TAG, "onDownloadFailed"); 80 | 81 | } 82 | 83 | @Override 84 | public void onProgress(int id, long totalBytes, int progress) { 85 | Log.e(TAG, "progress:" + progress); 86 | } 87 | }); 88 | mDownloadManager = new ThinDownloadManager(THREAD_COUNT); 89 | mDownloadManager.add(downloadRequest); 90 | } 91 | 92 | 93 | public void comparePath(Context context, PatchBean RemoteBean) throws Exception { 94 | String pathInfo = mLocalPreferencesHelper.getString(SPConst.PATH_INFO); 95 | final PatchBean localBean = GsonUtils.getInstance().parseIfNull(PatchBean.class, pathInfo); 96 | //远程的应用版本跟当前应用的版本比较 97 | if (BaseApplication.VERSION_NAME.equals(RemoteBean.app_v)) { 98 | //远程的应用版本跟本地保存的应用版本一样,但补丁不一样,则需要下载重新 99 | /** 100 | *第一种情况:当本地记录的Bean为空的时候(刚安装的时候可能为空)并且远程的Bean的path_v不为空的时候需要下载补丁。 101 | * 第二种情况:当本地记录的path_v和远程Bean的path_v不一样的时候需要下载补丁。 102 | */ 103 | if (localBean == null && !TextUtils.isEmpty(RemoteBean.path_v) 104 | || localBean.app_v.equals(RemoteBean.app_v) && 105 | !localBean.path_v.equals(RemoteBean.path_v)) { 106 | downloadAndLoad(context, RemoteBean, 107 | SPConst.URL_PREFIX + RemoteBean.url); 108 | String json = GsonUtils.getInstance().parse(RemoteBean); 109 | mLocalPreferencesHelper.saveOrUpdate(SPConst.PATH_INFO, json); 110 | } /*else { 111 | mLocalPreferencesHelper.saveOrUpdate(SPConst.IsHavePathDownLoad, false); 112 | }*/ 113 | } 114 | } 115 | 116 | public void release() { 117 | if (mDownloadManager != null) { 118 | mDownloadManager.release(); 119 | } 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /app/src/main/java/com/zhw/andfix/util/WeakHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Badoo Trading Limited 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy 4 | * of this software and associated documentation files (the "Software"), to deal 5 | * in the Software without restriction, including without limitation the rights 6 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | * copies of the Software, and to permit persons to whom the Software is 8 | * furnished to do so, subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in 11 | * all copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | * 21 | * Portions of documentation in this code are modifications based on work created and 22 | * shared by Android Open Source Project and used according to terms described in the 23 | * Apache License, Version 2.0 24 | */ 25 | package com.zhw.andfix.util; 26 | 27 | import android.os.Handler; 28 | import android.os.Looper; 29 | import android.os.Message; 30 | import android.support.annotation.NonNull; 31 | import android.support.annotation.Nullable; 32 | import android.support.annotation.VisibleForTesting; 33 | 34 | import java.lang.ref.WeakReference; 35 | import java.util.concurrent.locks.Lock; 36 | import java.util.concurrent.locks.ReentrantLock; 37 | 38 | /** 39 | * Memory safer implementation of android.os.Handler 40 | *

41 | * Original implementation of Handlers always keeps hard reference to handler in queue of execution. 42 | * If you create anonymous handler and post delayed message into it, it will keep all parent class 43 | * for that time in memory even if it could be cleaned. 44 | *

45 | * This implementation is trickier, it will keep WeakReferences to runnables and messages, 46 | * and GC could collect them once WeakHandler instance is not referenced any more 47 | *

48 | * 49 | * @see android.os.Handler 50 | * 51 | * Created by Dmytro Voronkevych on 17/06/2014. 52 | */ 53 | @SuppressWarnings("unused") 54 | public class WeakHandler { 55 | private final Handler.Callback mCallback; // hard reference to Callback. We need to keep callback in memory 56 | private final ExecHandler mExec; 57 | private Lock mLock = new ReentrantLock(); 58 | @SuppressWarnings("ConstantConditions") 59 | @VisibleForTesting 60 | final ChainedRef mRunnables = new ChainedRef(mLock, null); 61 | 62 | /** 63 | * Default constructor associates this handler with the {@link Looper} for the 64 | * current thread. 65 | * 66 | * If this thread does not have a looper, this handler won't be able to receive messages 67 | * so an exception is thrown. 68 | */ 69 | public WeakHandler() { 70 | mCallback = null; 71 | mExec = new ExecHandler(); 72 | } 73 | 74 | /** 75 | * Constructor associates this handler with the {@link Looper} for the 76 | * current thread and takes a callback interface in which you can handle 77 | * messages. 78 | * 79 | * If this thread does not have a looper, this handler won't be able to receive messages 80 | * so an exception is thrown. 81 | * 82 | * @param callback The callback interface in which to handle messages, or null. 83 | */ 84 | public WeakHandler(@Nullable Handler.Callback callback) { 85 | mCallback = callback; // Hard referencing body 86 | mExec = new ExecHandler(new WeakReference<>(callback)); // Weak referencing inside ExecHandler 87 | } 88 | 89 | /** 90 | * Use the provided {@link Looper} instead of the default one. 91 | * 92 | * @param looper The looper, must not be null. 93 | */ 94 | public WeakHandler(@NonNull Looper looper) { 95 | mCallback = null; 96 | mExec = new ExecHandler(looper); 97 | } 98 | 99 | /** 100 | * Use the provided {@link Looper} instead of the default one and take a callback 101 | * interface in which to handle messages. 102 | * 103 | * @param looper The looper, must not be null. 104 | * @param callback The callback interface in which to handle messages, or null. 105 | */ 106 | public WeakHandler(@NonNull Looper looper, @NonNull Handler.Callback callback) { 107 | mCallback = callback; 108 | mExec = new ExecHandler(looper, new WeakReference<>(callback)); 109 | } 110 | 111 | /** 112 | * Causes the Runnable r to be added to the message queue. 113 | * The runnable will be run on the thread to which this handler is 114 | * attached. 115 | * 116 | * @param r The Runnable that will be executed. 117 | * 118 | * @return Returns true if the Runnable was successfully placed in to the 119 | * message queue. Returns false on failure, usually because the 120 | * looper processing the message queue is exiting. 121 | */ 122 | public final boolean post(@NonNull Runnable r) { 123 | return mExec.post(wrapRunnable(r)); 124 | } 125 | 126 | /** 127 | * Causes the Runnable r to be added to the message queue, to be run 128 | * at a specific time given by uptimeMillis. 129 | * The time-base is {@link android.os.SystemClock#uptimeMillis}. 130 | * The runnable will be run on the thread to which this handler is attached. 131 | * 132 | * @param r The Runnable that will be executed. 133 | * @param uptimeMillis The absolute time at which the callback should run, 134 | * using the {@link android.os.SystemClock#uptimeMillis} time-base. 135 | * 136 | * @return Returns true if the Runnable was successfully placed in to the 137 | * message queue. Returns false on failure, usually because the 138 | * looper processing the message queue is exiting. Note that a 139 | * result of true does not mean the Runnable will be processed -- if 140 | * the looper is quit before the delivery time of the message 141 | * occurs then the message will be dropped. 142 | */ 143 | public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) { 144 | return mExec.postAtTime(wrapRunnable(r), uptimeMillis); 145 | } 146 | 147 | /** 148 | * Causes the Runnable r to be added to the message queue, to be run 149 | * at a specific time given by uptimeMillis. 150 | * The time-base is {@link android.os.SystemClock#uptimeMillis}. 151 | * The runnable will be run on the thread to which this handler is attached. 152 | * 153 | * @param r The Runnable that will be executed. 154 | * @param uptimeMillis The absolute time at which the callback should run, 155 | * using the {@link android.os.SystemClock#uptimeMillis} time-base. 156 | * 157 | * @return Returns true if the Runnable was successfully placed in to the 158 | * message queue. Returns false on failure, usually because the 159 | * looper processing the message queue is exiting. Note that a 160 | * result of true does not mean the Runnable will be processed -- if 161 | * the looper is quit before the delivery time of the message 162 | * occurs then the message will be dropped. 163 | * 164 | * @see android.os.SystemClock#uptimeMillis 165 | */ 166 | public final boolean postAtTime(Runnable r, Object token, long uptimeMillis) { 167 | return mExec.postAtTime(wrapRunnable(r), token, uptimeMillis); 168 | } 169 | 170 | /** 171 | * Causes the Runnable r to be added to the message queue, to be run 172 | * after the specified amount of time elapses. 173 | * The runnable will be run on the thread to which this handler 174 | * is attached. 175 | * 176 | * @param r The Runnable that will be executed. 177 | * @param delayMillis The delay (in milliseconds) until the Runnable 178 | * will be executed. 179 | * 180 | * @return Returns true if the Runnable was successfully placed in to the 181 | * message queue. Returns false on failure, usually because the 182 | * looper processing the message queue is exiting. Note that a 183 | * result of true does not mean the Runnable will be processed -- 184 | * if the looper is quit before the delivery time of the message 185 | * occurs then the message will be dropped. 186 | */ 187 | public final boolean postDelayed(Runnable r, long delayMillis) { 188 | return mExec.postDelayed(wrapRunnable(r), delayMillis); 189 | } 190 | 191 | /** 192 | * Posts a message to an object that implements Runnable. 193 | * Causes the Runnable r to executed on the next iteration through the 194 | * message queue. The runnable will be run on the thread to which this 195 | * handler is attached. 196 | * This method is only for use in very special circumstances -- it 197 | * can easily starve the message queue, cause ordering problems, or have 198 | * other unexpected side-effects. 199 | * 200 | * @param r The Runnable that will be executed. 201 | * 202 | * @return Returns true if the message was successfully placed in to the 203 | * message queue. Returns false on failure, usually because the 204 | * looper processing the message queue is exiting. 205 | */ 206 | public final boolean postAtFrontOfQueue(Runnable r) { 207 | return mExec.postAtFrontOfQueue(wrapRunnable(r)); 208 | } 209 | 210 | /** 211 | * Remove any pending posts of Runnable r that are in the message queue. 212 | */ 213 | public final void removeCallbacks(Runnable r) { 214 | final WeakRunnable runnable = mRunnables.remove(r); 215 | if (runnable != null) { 216 | mExec.removeCallbacks(runnable); 217 | } 218 | } 219 | 220 | /** 221 | * Remove any pending posts of Runnable r with Object 222 | * token that are in the message queue. If token is null, 223 | * all callbacks will be removed. 224 | */ 225 | public final void removeCallbacks(Runnable r, Object token) { 226 | final WeakRunnable runnable = mRunnables.remove(r); 227 | if (runnable != null) { 228 | mExec.removeCallbacks(runnable, token); 229 | } 230 | } 231 | 232 | /** 233 | * Pushes a message onto the end of the message queue after all pending messages 234 | * before the current time. It will be received in callback, 235 | * in the thread attached to this handler. 236 | * 237 | * @return Returns true if the message was successfully placed in to the 238 | * message queue. Returns false on failure, usually because the 239 | * looper processing the message queue is exiting. 240 | */ 241 | public final boolean sendMessage(Message msg) { 242 | return mExec.sendMessage(msg); 243 | } 244 | 245 | /** 246 | * Sends a Message containing only the what value. 247 | * 248 | * @return Returns true if the message was successfully placed in to the 249 | * message queue. Returns false on failure, usually because the 250 | * looper processing the message queue is exiting. 251 | */ 252 | public final boolean sendEmptyMessage(int what) { 253 | return mExec.sendEmptyMessage(what); 254 | } 255 | 256 | /** 257 | * Sends a Message containing only the what value, to be delivered 258 | * after the specified amount of time elapses. 259 | * @see #sendMessageDelayed(android.os.Message, long) 260 | * 261 | * @return Returns true if the message was successfully placed in to the 262 | * message queue. Returns false on failure, usually because the 263 | * looper processing the message queue is exiting. 264 | */ 265 | public final boolean sendEmptyMessageDelayed(int what, long delayMillis) { 266 | return mExec.sendEmptyMessageDelayed(what, delayMillis); 267 | } 268 | 269 | /** 270 | * Sends a Message containing only the what value, to be delivered 271 | * at a specific time. 272 | * @see #sendMessageAtTime(android.os.Message, long) 273 | * 274 | * @return Returns true if the message was successfully placed in to the 275 | * message queue. Returns false on failure, usually because the 276 | * looper processing the message queue is exiting. 277 | */ 278 | public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) { 279 | return mExec.sendEmptyMessageAtTime(what, uptimeMillis); 280 | } 281 | 282 | /** 283 | * Enqueue a message into the message queue after all pending messages 284 | * before (current time + delayMillis). You will receive it in 285 | * callback, in the thread attached to this handler. 286 | * 287 | * @return Returns true if the message was successfully placed in to the 288 | * message queue. Returns false on failure, usually because the 289 | * looper processing the message queue is exiting. Note that a 290 | * result of true does not mean the message will be processed -- if 291 | * the looper is quit before the delivery time of the message 292 | * occurs then the message will be dropped. 293 | */ 294 | public final boolean sendMessageDelayed(Message msg, long delayMillis) { 295 | return mExec.sendMessageDelayed(msg, delayMillis); 296 | } 297 | 298 | /** 299 | * Enqueue a message into the message queue after all pending messages 300 | * before the absolute time (in milliseconds) uptimeMillis. 301 | * The time-base is {@link android.os.SystemClock#uptimeMillis}. 302 | * You will receive it in callback, in the thread attached 303 | * to this handler. 304 | * 305 | * @param uptimeMillis The absolute time at which the message should be 306 | * delivered, using the 307 | * {@link android.os.SystemClock#uptimeMillis} time-base. 308 | * 309 | * @return Returns true if the message was successfully placed in to the 310 | * message queue. Returns false on failure, usually because the 311 | * looper processing the message queue is exiting. Note that a 312 | * result of true does not mean the message will be processed -- if 313 | * the looper is quit before the delivery time of the message 314 | * occurs then the message will be dropped. 315 | */ 316 | public boolean sendMessageAtTime(Message msg, long uptimeMillis) { 317 | return mExec.sendMessageAtTime(msg, uptimeMillis); 318 | } 319 | 320 | /** 321 | * Enqueue a message at the front of the message queue, to be processed on 322 | * the next iteration of the message loop. You will receive it in 323 | * callback, in the thread attached to this handler. 324 | * This method is only for use in very special circumstances -- it 325 | * can easily starve the message queue, cause ordering problems, or have 326 | * other unexpected side-effects. 327 | * 328 | * @return Returns true if the message was successfully placed in to the 329 | * message queue. Returns false on failure, usually because the 330 | * looper processing the message queue is exiting. 331 | */ 332 | public final boolean sendMessageAtFrontOfQueue(Message msg) { 333 | return mExec.sendMessageAtFrontOfQueue(msg); 334 | } 335 | 336 | /** 337 | * Remove any pending posts of messages with code 'what' that are in the 338 | * message queue. 339 | */ 340 | public final void removeMessages(int what) { 341 | mExec.removeMessages(what); 342 | } 343 | 344 | /** 345 | * Remove any pending posts of messages with code 'what' and whose obj is 346 | * 'object' that are in the message queue. If object is null, 347 | * all messages will be removed. 348 | */ 349 | public final void removeMessages(int what, Object object) { 350 | mExec.removeMessages(what, object); 351 | } 352 | 353 | /** 354 | * Remove any pending posts of callbacks and sent messages whose 355 | * obj is token. If token is null, 356 | * all callbacks and messages will be removed. 357 | */ 358 | public final void removeCallbacksAndMessages(Object token) { 359 | mExec.removeCallbacksAndMessages(token); 360 | } 361 | 362 | /** 363 | * Check if there are any pending posts of messages with code 'what' in 364 | * the message queue. 365 | */ 366 | public final boolean hasMessages(int what) { 367 | return mExec.hasMessages(what); 368 | } 369 | 370 | /** 371 | * Check if there are any pending posts of messages with code 'what' and 372 | * whose obj is 'object' in the message queue. 373 | */ 374 | public final boolean hasMessages(int what, Object object) { 375 | return mExec.hasMessages(what, object); 376 | } 377 | 378 | public final Looper getLooper() { 379 | return mExec.getLooper(); 380 | } 381 | 382 | private WeakRunnable wrapRunnable(@NonNull Runnable r) { 383 | //noinspection ConstantConditions 384 | if (r == null) { 385 | throw new NullPointerException("Runnable can't be null"); 386 | } 387 | final ChainedRef hardRef = new ChainedRef(mLock, r); 388 | mRunnables.insertAfter(hardRef); 389 | return hardRef.wrapper; 390 | } 391 | 392 | private static class ExecHandler extends Handler { 393 | private final WeakReference mCallback; 394 | 395 | ExecHandler() { 396 | mCallback = null; 397 | } 398 | 399 | ExecHandler(WeakReference callback) { 400 | mCallback = callback; 401 | } 402 | 403 | ExecHandler(Looper looper) { 404 | super(looper); 405 | mCallback = null; 406 | } 407 | 408 | ExecHandler(Looper looper, WeakReference callback) { 409 | super(looper); 410 | mCallback = callback; 411 | } 412 | 413 | @Override 414 | public void handleMessage(@NonNull Message msg) { 415 | if (mCallback == null) { 416 | return; 417 | } 418 | final Handler.Callback callback = mCallback.get(); 419 | if (callback == null) { // Already disposed 420 | return; 421 | } 422 | callback.handleMessage(msg); 423 | } 424 | } 425 | 426 | static class WeakRunnable implements Runnable { 427 | private final WeakReference mDelegate; 428 | private final WeakReference mReference; 429 | 430 | WeakRunnable(WeakReference delegate, WeakReference reference) { 431 | mDelegate = delegate; 432 | mReference = reference; 433 | } 434 | 435 | @Override 436 | public void run() { 437 | final Runnable delegate = mDelegate.get(); 438 | final ChainedRef reference = mReference.get(); 439 | if (reference != null) { 440 | reference.remove(); 441 | } 442 | if (delegate != null) { 443 | delegate.run(); 444 | } 445 | } 446 | } 447 | 448 | static class ChainedRef { 449 | @Nullable 450 | ChainedRef next; 451 | @Nullable 452 | ChainedRef prev; 453 | @NonNull 454 | final Runnable runnable; 455 | @NonNull 456 | final WeakRunnable wrapper; 457 | 458 | @NonNull 459 | Lock lock; 460 | 461 | public ChainedRef(@NonNull Lock lock, @NonNull Runnable r) { 462 | this.runnable = r; 463 | this.lock = lock; 464 | this.wrapper = new WeakRunnable(new WeakReference<>(r), new WeakReference<>(this)); 465 | } 466 | 467 | public WeakRunnable remove() { 468 | lock.lock(); 469 | try { 470 | if (prev != null) { 471 | prev.next = next; 472 | } 473 | if (next != null) { 474 | next.prev = prev; 475 | } 476 | prev = null; 477 | next = null; 478 | } finally { 479 | lock.unlock(); 480 | } 481 | return wrapper; 482 | } 483 | 484 | public void insertAfter(@NonNull ChainedRef candidate) { 485 | lock.lock(); 486 | try { 487 | if (this.next != null) { 488 | this.next.prev = candidate; 489 | } 490 | 491 | candidate.next = this.next; 492 | this.next = candidate; 493 | candidate.prev = this; 494 | } finally { 495 | lock.unlock(); 496 | } 497 | } 498 | 499 | @Nullable 500 | public WeakRunnable remove(Runnable obj) { 501 | lock.lock(); 502 | try { 503 | ChainedRef curr = this.next; // Skipping head 504 | while (curr != null) { 505 | if (curr.runnable == obj) { // We do comparison exactly how Handler does inside 506 | return curr.remove(); 507 | } 508 | curr = curr.next; 509 | } 510 | } finally { 511 | lock.unlock(); 512 | } 513 | return null; 514 | } 515 | } 516 | } -------------------------------------------------------------------------------- /app/src/main/jniLibs/arm64-v8a/libandfix.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhonghanwen/AndFix-Bad-Practices/5471ea037fe0512608e93ff61a632c32e6bf9986/app/src/main/jniLibs/arm64-v8a/libandfix.so -------------------------------------------------------------------------------- /app/src/main/jniLibs/arm64-v8a/libjpush210.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhonghanwen/AndFix-Bad-Practices/5471ea037fe0512608e93ff61a632c32e6bf9986/app/src/main/jniLibs/arm64-v8a/libjpush210.so -------------------------------------------------------------------------------- /app/src/main/jniLibs/armeabi-v7a/libandfix.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhonghanwen/AndFix-Bad-Practices/5471ea037fe0512608e93ff61a632c32e6bf9986/app/src/main/jniLibs/armeabi-v7a/libandfix.so -------------------------------------------------------------------------------- /app/src/main/jniLibs/armeabi-v7a/libjpush210.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhonghanwen/AndFix-Bad-Practices/5471ea037fe0512608e93ff61a632c32e6bf9986/app/src/main/jniLibs/armeabi-v7a/libjpush210.so -------------------------------------------------------------------------------- /app/src/main/jniLibs/armeabi/libjpush210.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhonghanwen/AndFix-Bad-Practices/5471ea037fe0512608e93ff61a632c32e6bf9986/app/src/main/jniLibs/armeabi/libjpush210.so -------------------------------------------------------------------------------- /app/src/main/jniLibs/mips/libandfix.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhonghanwen/AndFix-Bad-Practices/5471ea037fe0512608e93ff61a632c32e6bf9986/app/src/main/jniLibs/mips/libandfix.so -------------------------------------------------------------------------------- /app/src/main/jniLibs/mips/libjpush210.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhonghanwen/AndFix-Bad-Practices/5471ea037fe0512608e93ff61a632c32e6bf9986/app/src/main/jniLibs/mips/libjpush210.so -------------------------------------------------------------------------------- /app/src/main/jniLibs/mips64/libandfix.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhonghanwen/AndFix-Bad-Practices/5471ea037fe0512608e93ff61a632c32e6bf9986/app/src/main/jniLibs/mips64/libandfix.so -------------------------------------------------------------------------------- /app/src/main/jniLibs/mips64/libjpush210.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhonghanwen/AndFix-Bad-Practices/5471ea037fe0512608e93ff61a632c32e6bf9986/app/src/main/jniLibs/mips64/libjpush210.so -------------------------------------------------------------------------------- /app/src/main/jniLibs/x86/libjpush210.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhonghanwen/AndFix-Bad-Practices/5471ea037fe0512608e93ff61a632c32e6bf9986/app/src/main/jniLibs/x86/libjpush210.so -------------------------------------------------------------------------------- /app/src/main/jniLibs/x86_64/libandfix.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhonghanwen/AndFix-Bad-Practices/5471ea037fe0512608e93ff61a632c32e6bf9986/app/src/main/jniLibs/x86_64/libandfix.so -------------------------------------------------------------------------------- /app/src/main/jniLibs/x86_64/libjpush210.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhonghanwen/AndFix-Bad-Practices/5471ea037fe0512608e93ff61a632c32e6bf9986/app/src/main/jniLibs/x86_64/libjpush210.so -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/border_bg.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhonghanwen/AndFix-Bad-Practices/5471ea037fe0512608e93ff61a632c32e6bf9986/app/src/main/res/drawable-hdpi/border_bg.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/bottom_bg.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhonghanwen/AndFix-Bad-Practices/5471ea037fe0512608e93ff61a632c32e6bf9986/app/src/main/res/drawable-hdpi/bottom_bg.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/corners_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhonghanwen/AndFix-Bad-Practices/5471ea037fe0512608e93ff61a632c32e6bf9986/app/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_richpush_actionbar_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhonghanwen/AndFix-Bad-Practices/5471ea037fe0512608e93ff61a632c32e6bf9986/app/src/main/res/drawable-hdpi/ic_richpush_actionbar_back.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_richpush_actionbar_divider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhonghanwen/AndFix-Bad-Practices/5471ea037fe0512608e93ff61a632c32e6bf9986/app/src/main/res/drawable-hdpi/ic_richpush_actionbar_divider.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/jpush_notification_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhonghanwen/AndFix-Bad-Practices/5471ea037fe0512608e93ff61a632c32e6bf9986/app/src/main/res/drawable-hdpi/jpush_notification_icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/richpush_btn_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 14 | 15 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/stripes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhonghanwen/AndFix-Bad-Practices/5471ea037fe0512608e93ff61a632c32e6bf9986/app/src/main/res/drawable-hdpi/stripes.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/tiledstripes.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/top_bg.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhonghanwen/AndFix-Bad-Practices/5471ea037fe0512608e93ff61a632c32e6bf9986/app/src/main/res/drawable-hdpi/top_bg.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-ldpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhonghanwen/AndFix-Bad-Practices/5471ea037fe0512608e93ff61a632c32e6bf9986/app/src/main/res/drawable-ldpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhonghanwen/AndFix-Bad-Practices/5471ea037fe0512608e93ff61a632c32e6bf9986/app/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhonghanwen/AndFix-Bad-Practices/5471ea037fe0512608e93ff61a632c32e6bf9986/app/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 |