Android干货功能专区Android进阶之路

最简单的腾讯Bugly集成

2018-01-19  本文已影响127人  一s独秀

简单的记录下在快速集成Bugly是遇到过的坑,更多说明去官方文档,此文章,简单的集成bugly实现了,异常统计,应用更新以及热修复

好了,到这里啦异常统计肯定没问题了,看看初始化
 /**
         * 单个异常上传统计时的初始化方法
         * @appContext 上下文
         * @crashReportAppId 注册时申请的APPID
         * @isDebug 自定义日志将会在Logcat中输出。
         */
        CrashReport.initCrashReport(getApplicationContext(), Commont.APP_ID, false);

        /**
         * 统一初始化Bugly产品,包含Beta
         * @context 上下文
         * @appId 注册时申请的APPID
         * @isDebug 自定义日志将会在Logcat中输出。
         *
         * 提示:已经接入Bugly用户改用上面的初始化方法,不影响原有的crash上报功能; init方法会自动检测更新,不需要再手动调用Beta.checkUpgrade(), 如需增加自动检查时机可以使用Beta.checkUpgrade(false,false);
         * 参数1:isManual 用户手动点击检查,非用户点击操作请传false
         * 参数2:isSilence 是否显示弹窗等交互,[true:没有弹窗和toast] [false:有弹窗或toast]
         */
        Bugly.init(this, Commont.APP_ID, true);


        /**
         * 统一初始化Bugly产品,包含Beta
         * @context 上下文
         * @appId 注册时申请的APPID
         * @isDebug 自定义日志将会在Logcat中输出。
         * @strategy Bugly高级设置
         */
        Bugly.init(this, Commont.APP_ID, true, strategy);
到这里了,可以说应用升级也基本已经完成了,我们在到Manifest里面为升级补全配置
 <!-- bugly配置权限start -->
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.READ_LOGS" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <!-- bugly配置权限end -->

<application
        android:name=".applications.SampleApplication"
        android:allowBackup="true"
        android:icon="@mipmap/logo"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <!-- Bugly升级SDK配置开始 -->
        <activity
            android:name="com.tencent.bugly.beta.ui.BetaActivity"
            android:theme="@android:style/Theme.Translucent" />

        <!-- 想兼容Android N或者以上的设备 -->
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="(你自己的包名).fileProvider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths" />
        </provider>
        <meta-data
            android:name="UMENG_APPKEY"
            android:value="5a6059*****000091" /><!--友盟 Appkey 自己应用注册申请来的-->
        <meta-data
            android:name="UMENG_CHANNEL"
            android:value="${UMENG_CHANNEL_VALUE}" /> <!--渠道号,多渠道这里使用了占位符$-->
    </application>
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 这里配置的两个外部存储路径是升级SDK下载的文件可能存在的路径 -->
    <!-- /storage/emulated/0/Download/com.bugly.upgrade.demo/.beta/apk-->
    <external-path name="beta_external_path" path="Download/"/>
    <!--/storage/emulated/0/Android/data/com.bugly.upgrade.demo/files/apk/-->
    <external-path name="beta_external_files_path" path="Android/data/"/>
</paths>
ok,升级也完成了, 看看最后打补丁-----热修复
apply plugin: 'com.tencent.bugly.tinker-support'

def bakPath = file("${buildDir}/bakApk/")
/**
 * 此处填写每次构建生成的基准包目录
 */
def baseApkDir = "app-0119-10-02-31"

/**
 * 对于插件各参数的详细解析请参考
 */
tinkerSupport {
    // 开启tinker-support插件,默认值true
    enable = true

    // tinkerEnable功能开关
    tinkerEnable = true
    // 是否编译完成后,归档apk到指定目录,默认值false

    // 指定归档目录,默认值当前module的子目录tinker
    autoBackupApkDir = "${bakPath}"

    // 是否启用覆盖tinkerPatch配置功能,默认值false
    // 开启后tinkerPatch配置不生效,即无需添加tinkerPatch
    overrideTinkerPatchConfiguration = true

    // 编译补丁包时,必需指定基线版本的apk,默认值为空
    // 如果为空,则表示不是进行补丁包的编译
    // @{link tinkerPatch.oldApk }
    baseApk = "${bakPath}/${baseApkDir}/app-release.apk"

    // 对应tinker插件applyMapping
    baseApkProguardMapping = "${bakPath}/${baseApkDir}/app-release-mapping.txt"

    // 对应tinker插件applyResourceMapping
    baseApkResourceMapping = "${bakPath}/${baseApkDir}/app-release-R.txt"

    // 构建基准包和补丁包都要指定不同的tinkerId,并且必须保证唯一性
//    tinkerId = "1.0.0.4-base"//生成对于的基包版本  只有生成基准包的时候使用
    tinkerId = "1.0.0.4-patch"//对于生成的补丁包版本  只有生成补丁包的时候使用

    // 构建多渠道补丁时使用
    buildAllFlavorsDir = "${bakPath}/${baseApkDir}"

    // 是否启用加固模式,默认为false.(tinker-spport 1.0.7起支持)
    // isProtectedApp = true

    // 是否开启反射Application模式
    enableProxyApplication = false

   // 是否支持新增非export的Activity(注意:设置为true才能修改AndroidManifest文件)
    supportHotplugComponent = true
}

/**
 * 一般来说,我们无需对下面的参数做任何的修改
 * 对于各参数的详细介绍请参考:
 * https://github.com/Tencent/tinker/wiki/Tinker-%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97
 */
tinkerPatch {
    tinkerEnable = true
    //oldApk ="${bakPath}/${appName}/app-release.apk"
    ignoreWarning = false
    //对应tinker插件userSign, 在运行过程中,
    // 我们需要验证基准apk包与补丁包的签名是否一致,我们是否需要为你签名。
    useSign = true
    dex {
        dexMode = "jar"
        pattern = ["classes*.dex"]
        loader = []
    }
    lib {
        pattern = ["lib/*/*.so"]
    }

    res {
        pattern = ["res/*", "r/*", "assets/*", "resources.arsc", "AndroidManifest.xml"]
        ignoreChange = []
        largeModSize = 100
    }

    // 用于生成补丁包中的'package_meta.txt'文件
    packageConfig {
//        configField("patchMessage", "tinker is sample to use")
//        configField("platform", "all")
//        configField("patchVersion", "1.0.5")
    }
    sevenZip {
        zipArtifact = "com.tencent.mm:SevenZip:1.1.10"
//        path = "/usr/local/bin/7za"
    }
    buildConfig {
        keepDexApply = false
        //tinkerId = "1.0.1-base"
        //applyMapping = "${bakPath}/${appName}/app-release-mapping.txt" //  可选,设置mapping文件,建议保持旧apk的proguard混淆方式
        //applyResourceMapping = "${bakPath}/${appName}/app-release-R.txt" // 可选,设置R.txt文件,通过旧apk文件保持ResId的分配
    }
}
这种情况下分为两种, enableProxyApplication默认为false
 public SampleApplication() {
        //记住此处com.suchengkeji.android.bugly.applications.SampleApplicationLike改为你自己新建的路径包名
        super(ShareConstants.TINKER_ENABLE_ALL, "com.suchengkeji.android.bugly.applications.SampleApplicationLike",
                "com.tencent.tinker.loader.TinkerLoader", false);
    }

 @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    @Override
    public void onBaseContextAttached(Context base) {
        super.onBaseContextAttached(base);
        // you must install multiDex whatever tinker is installed!
        MultiDex.install(base);

        // 安装tinker
        // TinkerManager.installTinker(this); 替换成下面Bugly提供的方法
        Beta.installTinker(this);
    }

    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    public void registerActivityLifecycleCallback(Application.ActivityLifecycleCallbacks callbacks) {
        getApplication().registerActivityLifecycleCallbacks(callbacks);
    }

③ 将启动Application改为SampleApplication

manifest.png
   @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        // you must install multiDex whatever tinker is installed!
        MultiDex.install(base);

        // 安装tinker
        // TinkerManager.installTinker(this); 替换成下面Bugly提供的方法
        Beta.installTinker(this);
    }

② 在onCreate()方法中可初始化设置Bugly一些鬼

这里额外说下Application启动时流程: attachBaseContext(Context) -----> onCreate()

③ 'Applaction'启动项改回来,改为我们自定义的


manifest2.png

到这里也就接近完了,现在看看怎么创建基准包和补丁包

基准包、mapping以及补丁包的上传

我们生成的基准包 启动
mapping的上传
image.png
最后贴出以本Demo为例的源码
public class MyApplication extends Application {

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        // you must install multiDex whatever tinker is installed!
        MultiDex.install(base);

        // 安装tinker
        // TinkerManager.installTinker(this); 替换成下面Bugly提供的方法
        Beta.installTinker(this);
    }

    @Override
    public void onCreate() {
        super.onCreate();

        setBugly();
    }

    private void setBugly() {
        /***** Beta高级设置 *****/
        /**
         * true表示app启动自动初始化升级模块; false不会自动初始化;
         * 开发者如果担心sdk初始化影响app启动速度,可以设置为false,
         * 在后面某个时刻手动调用Beta.init(getApplicationContext(),false);
         */
        Beta.autoInit = true;

        /**
         * true表示初始化时自动检查升级; false表示不会自动检查升级,需要手动调用Beta.checkUpgrade()方法;
         */
        Beta.autoCheckUpgrade = true;

        /**
         * 设置升级检查周期为60s(默认检查周期为0s),60s内SDK不重复向后台请求策略);
         */
        Beta.upgradeCheckPeriod = 60 * 1000;
        /**
         * 设置启动延时为1s(默认延时3s),APP启动1s后初始化SDK,避免影响APP启动速度;
         */
        Beta.initDelay = 1 * 1000;
        /**
         * 设置通知栏大图标,largeIconId为项目中的图片资源;
         */
        Beta.largeIconId = R.mipmap.ic_launcher;
        /**
         * 设置状态栏小图标,smallIconId为项目中的图片资源Id;
         */
        Beta.smallIconId = R.mipmap.ic_launcher;
        /**
         * 设置更新弹窗默认展示的banner,defaultBannerId为项目中的图片资源Id;
         * 当后台配置的banner拉取失败时显示此banner,默认不设置则展示“loading“;
         */
        Beta.defaultBannerId = R.mipmap.ic_launcher;
        /**
         * 设置sd卡的Download为更新资源保存目录;
         * 后续更新资源会保存在此目录,需要在manifest中添加WRITE_EXTERNAL_STORAGE权限;
         */
        Beta.storageDir = Environment
                .getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
        /**
         * 已经确认过的弹窗在APP下次启动自动检查更新时会再次显示;
         */
        Beta.showInterruptedStrategy = true;
        /**
         * 只允许在MainActivity上显示更新弹窗,其他activity上不显示弹窗; 不设置会默认所有activity都可以显示弹窗;
         */
        Beta.canShowUpgradeActs.add(MainActivity.class);

        /***** Bugly高级设置 *****/
        BuglyStrategy strategy = new BuglyStrategy();
        /**
         * 设置app渠道号
         */
        strategy.setAppChannel(ChannelUtils.getAppMetaData(this, "UMENG_CHANNEL"));

//        /**
//         * 单个异常上传统计时的初始化方法
//         * @appContext 上下文
//         * @crashReportAppId 注册时申请的APPID
//         * @isDebug 自定义日志将会在Logcat中输出。
//         */
//        CrashReport.initCrashReport(getApplicationContext(), Commont.APP_ID, false);
//
//        /**
//         * 统一初始化Bugly产品,包含Beta
//         * @context 上下文
//         * @appId 注册时申请的APPID
//         * @isDebug 自定义日志将会在Logcat中输出。
//         *
//         * 提示:已经接入Bugly用户改用上面的初始化方法,不影响原有的crash上报功能; init方法会自动检测更新,不需要再手动调用Beta.checkUpgrade(), 如需增加自动检查时机可以使用Beta.checkUpgrade(false,false);
//         * 参数1:isManual 用户手动点击检查,非用户点击操作请传false
//         * 参数2:isSilence 是否显示弹窗等交互,[true:没有弹窗和toast] [false:有弹窗或toast]
//         */
//        Bugly.init(this, Commont.APP_ID, true);


        /**
         * 统一初始化Bugly产品,包含Beta
         * @context 上下文
         * @appId 注册时申请的APPID
         * @isDebug 自定义日志将会在Logcat中输出。
         * @strategy Bugly高级设置
         */
        Bugly.init(this, Commont.APP_ID, true, strategy);
    }
}
/**
 * 注意:这个类集成TinkerApplication类,这里面不做任何操作,所有Application的代码都会放到ApplicationLike继承类当中
 * 参数解析
 * 参数1:tinkerFlags 表示Tinker支持的类型 dex only、library only or all suuport,default: TINKER_ENABLE_ALL
 * 参数2:delegateClassName Application代理类 这里填写你自定义的ApplicationLike
 * 参数3:loaderClassName Tinker的加载器,使用默认即可
 * 参数4:tinkerLoadVerifyFlag 加载dex或者lib是否验证md5,默认为false
 */
public class SampleApplication extends TinkerApplication {

    public SampleApplication() {
        //将‘com.suchengkeji.android.bugly.applications.SampleApplicationLike’替换为自己的包名及路径
        super(ShareConstants.TINKER_ENABLE_ALL, "com.suchengkeji.android.bugly.applications.SampleApplicationLike",
                "com.tencent.tinker.loader.TinkerLoader", false);
    }
}
public class SampleApplicationLike extends DefaultApplicationLike {
    public SampleApplicationLike(Application application, int tinkerFlags,
                                 boolean tinkerLoadVerifyFlag, long applicationStartElapsedTime,
                                 long applicationStartMillisTime, Intent tinkerResultIntent) {
        super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime, applicationStartMillisTime, tinkerResultIntent);
    }


    @Override
    public void onCreate() {
        super.onCreate();
        // 这里实现SDK初始化,appId替换成你的在Bugly平台申请的appId
        // 调试时,将第三个参数改为true
//        Bugly.init(getApplication(), APP_ID, false);
        setBUG();
    }


    private void setBUG() {
        /***** Beta高级设置 *****/
        /**
         * true表示app启动自动初始化升级模块; false不会自动初始化;
         * 开发者如果担心sdk初始化影响app启动速度,可以设置为false,
         * 在后面某个时刻手动调用Beta.init(getApplicationContext(),false);
         */
        Beta.autoInit = true;

        /**
         * true表示初始化时自动检查升级; false表示不会自动检查升级,需要手动调用Beta.checkUpgrade()方法;
         */
        Beta.autoCheckUpgrade = true;

        /**
         * 设置升级检查周期为60s(默认检查周期为0s),60s内SDK不重复向后台请求策略);
         */
        Beta.upgradeCheckPeriod = 60 * 1000;
        /**
         * 设置启动延时为1s(默认延时3s),APP启动1s后初始化SDK,避免影响APP启动速度;
         */
        Beta.initDelay = 1 * 1000;
        /**
         * 设置通知栏大图标,largeIconId为项目中的图片资源;
         */
        Beta.largeIconId = R.mipmap.ic_launcher;
        /**
         * 设置状态栏小图标,smallIconId为项目中的图片资源Id;
         */
        Beta.smallIconId = R.mipmap.ic_launcher;
        /**
         * 设置更新弹窗默认展示的banner,defaultBannerId为项目中的图片资源Id;
         * 当后台配置的banner拉取失败时显示此banner,默认不设置则展示“loading“;
         */
        Beta.defaultBannerId = R.mipmap.ic_launcher;
        /**
         * 设置sd卡的Download为更新资源保存目录;
         * 后续更新资源会保存在此目录,需要在manifest中添加WRITE_EXTERNAL_STORAGE权限;
         */
        Beta.storageDir = Environment
                .getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
        /**
         * 已经确认过的弹窗在APP下次启动自动检查更新时会再次显示;
         */
        Beta.showInterruptedStrategy = true;
        /**
         * 只允许在MainActivity上显示更新弹窗,其他activity上不显示弹窗; 不设置会默认所有activity都可以显示弹窗;
         */
        Beta.canShowUpgradeActs.add(MainActivity.class);

        /***** Bugly高级设置 *****/
        BuglyStrategy strategy = new BuglyStrategy();
        /**
         * 设置app渠道号
         * ChannelUtils.getAppMetaData(this, "UMENG_CHANNEL")获取渠道号
         */
        strategy.setAppChannel(ChannelUtils.getAppMetaData(getApplication(), "UMENG_CHANNEL"));

        /***** 统一初始化Bugly产品,包含Beta *****/
        Bugly.init(getApplication(), Commont.APP_ID, true, strategy);
    }

    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    @Override
    public void onBaseContextAttached(Context base) {
        super.onBaseContextAttached(base);
        // you must install multiDex whatever tinker is installed!
        MultiDex.install(base);

        // 安装tinker
        // TinkerManager.installTinker(this); 替换成下面Bugly提供的方法
        Beta.installTinker(this);
    }

    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    public void registerActivityLifecycleCallback(Application.ActivityLifecycleCallbacks callbacks) {
        getApplication().registerActivityLifecycleCallbacks(callbacks);
    }
}
public class MainActivity extends AppCompatActivity implements AdapterView.OnItemClickListener {

    @BindView(R.id.list_view)
    ListView listView;

    String[] strings = {"异常上报", "应用更新", "热修复测试"};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        setList();
    }

    private void setList() {
        listView.setAdapter(new BaseAdapter() {
            @Override
            public int getCount() {
                return strings.length;
            }

            @Override
            public Object getItem(int position) {
                return strings[position];
            }

            @Override
            public long getItemId(int position) {
                return position;
            }

            @Override
            public View getView(int position, View convertView, ViewGroup parent) {
                @SuppressLint("ViewHolder") View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.list_item, parent, false);
                TextView textView = (TextView) view.findViewById(R.id.list_item_text);
                textView.setText(strings[position]);
                return view;
            }
        });
        listView.setOnItemClickListener(this);
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        Log.d(LogUtils.TAG(), position + "");
        switch (position) {
            case 0:
                /***** 异常崩溃上传 *****/
                CrashReport.testJavaCrash();
                break;
            case 1:
                /***** 检查更新 *****/
                loadUpgradeInfo();
                loadAppInfo();
                Beta.checkUpgrade();
                break;
            case 2:
                /***** 崩溃上传(此处要打修复补丁) *****/
                FixBugClass.getTimer(MainActivity.this);
//                BugClass.getTimer(MainActivity.this);
                break;
        }
    }

    @Override
    protected void onStart() {
        super.onStart();

        //添加权限
        PermissionGen.with(this)
                .addRequestCode(Commont.PHOTO_PERMISS)
                .permissions(
                        Manifest.permission.INTERNET,
                        Manifest.permission.READ_PHONE_STATE,
                        Manifest.permission.ACCESS_NETWORK_STATE,
                        Manifest.permission.ACCESS_WIFI_STATE,
                        Manifest.permission.READ_LOGS,
                        Manifest.permission.WRITE_EXTERNAL_STORAGE)
                .request();
    }

    @PermissionSuccess(requestCode = Commont.PHOTO_PERMISS)
    public void requestPhotoSuccess() {
        //成功之后的处理
        //.......
        Log.d(LogUtils.TAG(), "动态权限加载成功");
    }

    @PermissionFail(requestCode = Commont.PHOTO_PERMISS)
    public void requestPhotoFail() {
        //失败之后的处理,我一般是跳到设置界面
        Log.d(LogUtils.TAG(), "动态权限加载失败");
    }


    /**
     * 获取升级信息
     */
    private void loadUpgradeInfo() {
        /***** 获取升级信息 *****/
        UpgradeInfo upgradeInfo = Beta.getUpgradeInfo();

        if (upgradeInfo == null) {
            Log.d(LogUtils.TAG(), "无升级信息");
            return;
        }
        StringBuilder info = new StringBuilder();
        info.append("id: ").append(upgradeInfo.id).append("\n");
        info.append("标题: ").append(upgradeInfo.title).append("\n");
        info.append("升级说明: ").append(upgradeInfo.newFeature).append("\n");
        info.append("versionCode: ").append(upgradeInfo.versionCode).append("\n");
        info.append("versionName: ").append(upgradeInfo.versionName).append("\n");
        info.append("发布时间: ").append(upgradeInfo.publishTime).append("\n");
        info.append("安装包Md5: ").append(upgradeInfo.apkMd5).append("\n");
        info.append("安装包下载地址: ").append(upgradeInfo.apkUrl).append("\n");
        info.append("安装包大小: ").append(upgradeInfo.fileSize).append("\n");
        info.append("弹窗间隔(ms): ").append(upgradeInfo.popInterval).append("\n");
        info.append("弹窗次数: ").append(upgradeInfo.popTimes).append("\n");
        info.append("发布类型(0:测试 1:正式): ").append(upgradeInfo.publishType).append("\n");
        info.append("弹窗类型(1:建议 2:强制 3:手工): ").append(upgradeInfo.upgradeType);
        Log.d(LogUtils.TAG(), info + "");
    }

    private void loadAppInfo() {
        try {
            StringBuilder info = new StringBuilder();
            PackageInfo packageInfo =
                    this.getPackageManager().getPackageInfo(this.getPackageName(),
                            PackageManager.GET_CONFIGURATIONS);
            int versionCode = packageInfo.versionCode;
            String versionName = packageInfo.versionName;
            info.append("appid: ").append(Commont.APP_ID).append(" ")
                    .append("channel: ")
                    .append(ChannelUtils.getAppMetaData(MainActivity.this, "UMENG_CHANNEL"))
                    .append(" ")
                    .append("version: ").append(versionName).append(".").append(versionCode);
            Log.d(LogUtils.TAG(), info + "");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
public class Commont {
    /***   腾讯Bugly产品ID   ***/
    public static final String APP_ID = "5e****70c08";

    /***   权限处理标识   ***/
    public static final int PHOTO_PERMISS = 100;
}
 /**
     * 获取app当前的渠道号或application中指定的meta-data
     *
     * @return 如果没有获取成功(没有对应值,或者异常),则返回值为空
     */
    public static String getAppMetaData(Context context, String key) {
        if (context == null || TextUtils.isEmpty(key)) {
            return null;
        }
        String channelNumber = null;
        try {
            PackageManager packageManager = context.getPackageManager();
            if (packageManager != null) {
                ApplicationInfo applicationInfo = packageManager.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
                if (applicationInfo != null) {
                    if (applicationInfo.metaData != null) {
                        channelNumber = applicationInfo.metaData.getString(key);
                    }
                }
            }
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return channelNumber;
    }

//      在需要的地方调用
//    String channelNumber = getAppMetaData(getBaseContext(), "UMENG_CHANNEL");//获取app当前的渠道号
}
/**
 * 有错误的bug类
 */
public class BugClass {
    private static String timeType = "yyyy-MM-dd HH:mm:ss";

    public static void getTimer(Context c) {
        String date = null;
        Log.d(LogUtils.TAG(), "上线后的紧急BUG");
        Log.d(LogUtils.TAG(), date.length() + "");//此处留一个空指针bug
        @SuppressLint("SimpleDateFormat")
        SimpleDateFormat sDateFormat = new SimpleDateFormat(timeType);
        date = sDateFormat.format(new java.util.Date());
        Toast.makeText(c, date, Toast.LENGTH_SHORT).show();
    }
}
/**
 * 修复过bug的补丁类
 */
public class FixBugClass {
    private static String timeType = "yyyy-MM-dd HH:mm:ss";

    public static void getTimer(Context c) {
        String date = null;
        Log.d(LogUtils.TAG(), "上线后的紧急BUG修复补丁");
        //Log.d(LogUtils.TAG(), date.length() + "");//去掉此处的bug
        @SuppressLint("SimpleDateFormat")
        SimpleDateFormat sDateFormat = new SimpleDateFormat(timeType);
        date = sDateFormat.format(new java.util.Date());
        Toast.makeText(c, date, Toast.LENGTH_SHORT).show();
    }
}

注意点,打完补丁之后在运行应用是,第一次仍然会崩溃,不过在崩溃时会自己打补丁修复,第二次以后就好了
好了,到这里就完了,此文章仅供参考,具体以官方文档为准,有更好的思路或方法,请不吝告知,如有错误,请及时指出。。。

上一篇下一篇

猜你喜欢

热点阅读