1. 程式人生 > >Android實戰——Tinker的整合和使用

Android實戰——Tinker的整合和使用

前言

對於熱修復我相信很多小夥伴都已經知道它們普遍的操作套路,Tinker主要是依賴自己的gradlePlugin生成拆分包,所以其拆分包的生成就由Gradle來完成,當然也可以通過命令列的方式,這裡就不對命令列做講解,Tinker接入指南

專案結構

這裡寫圖片描述

Tinker介紹

來自Tinker官方

1、優點

這裡寫圖片描述

2、缺點

  1. Tinker不支援修改AndroidManifest.xml,Tinker不支援新增四大元件(1.9.0支援新增非export的Activity);
  2. 由於Google Play的開發者條款限制,不建議在GP渠道動態更新程式碼;
  3. 在Android N上,補丁對應用啟動時間有輕微的影響;
  4. 不支援部分三星android-21機型,載入補丁時會主動丟擲”TinkerRuntimeException:checkDexInstall failed”;
  5. 對於資源替換,不支援修改remoteView。例如transition動畫,notification icon以及桌面圖示。

Tinker整合

1、在專案的build.gradle中,新增依賴

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.3.3'
// Tinker classpath ("com.tencent.tinker:tinker-patch-gradle-plugin:${TINKER_VERSION}") } }

這裡的TINKER_VERSION寫在專案gradle.properties中

TINKER_VERSION=1.7.7

2、在app的build.gradle檔案,新增依賴

provided("com.tencent.tinker:tinker-android-anno:${TINKER_VERSION}")
compile("com.tencent.tinker:tinker-android-lib:${TINKER_VERSION}
"
) compile "com.android.support:multidex:1.0.1"

新增依賴以後,我們在gradle檔案中做以下配置

  1. 開啟Multidex、配置Java編譯的版本
  2. 配置簽名檔案,為了後面打包方便除錯
  3. 引入另一個gradle檔案專門來對Tinker生成拆分包的配置(由於多渠道要用到gradle的引數,所以將引入放在末尾)
apply plugin: 'com.android.application'

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.3"
    defaultConfig {
        applicationId "com.handsome.thinker"
        minSdkVersion 16
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        multiDexEnabled true
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_7
        targetCompatibility JavaVersion.VERSION_1_7
    }
    dexOptions {
        jumboMode = true
    }
    signingConfigs {
        debug {
            keyAlias 'hensen'
            keyPassword '123456'
            storeFile file("../Hensen.jks")
            storePassword '123456'
        }
        release {
            keyAlias 'hensen'
            keyPassword '123456'
            storeFile file("../Hensen.jks")
            storePassword '123456'
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.release
        }
        debug {
            minifyEnabled false
            signingConfig signingConfigs.debug
        }
    }
}
// 加入Tinker生成補丁包的gradle
apply from: 'buildTinker.gradle'

3、buildTinker.gradle是專門為Tinker配置和生成拆分包而寫的,具體可以參考官方gradle

//指定生成apk檔案的存放位置
def bakPath = file("${buildDir}/bakApk/")
//引數配置
ext {
    //開啟Tinker
    tinkerEnable = true
    //舊的apk位置,需要我們手動指定
    tinkerOldApkPath = "${bakPath}/"
    //舊的混淆對映位置,如果開啟了混淆,則需要我們手動指定
    tinkerApplyMappingPath = "${bakPath}/"
    //舊的resource位置,需要我們手動指定
    tinkerApplyResourcePath = "${bakPath}/"
    tinkerID = "1.0"
}

def buildWithTinker() {
    return ext.tinkerEnable
}

def getOldApkPath() {
    return ext.tinkerOldApkPath
}

def getApplyMappingPath() {
    return ext.tinkerApplyMappingPath
}

def getApplyResourceMappingPath(){
    return ext.tinkerApplyResourcePath
}

def getTinkerIdValue(){
    return ext.tinkerID
}

if (buildWithTinker()) {
    apply plugin: 'com.tencent.tinker.patch'

    tinkerPatch {
        oldApk = getOldApkPath() //指定old apk檔案路徑
        ignoreWarning = false //不忽略tinker警告,出現警告則中止patch檔案生成
        useSign = true //patch檔案必須是簽名後的
        tinkerEnable = buildWithTinker() //指定是否啟用tinker
        buildConfig {
            applyMapping = getApplyMappingPath() //指定old apk打包時所使用的混淆檔案
            applyResourceMapping = getApplyResourceMappingPath() //指定old apk的資原始檔
            tinkerId = getTinkerIdValue() //指定TinkerID
            keepDexApply = false
        }
        dex {
            dexMode = "jar" //jar、raw
            pattern = ["classes*.dex", "assets/secondary-dex-?.jar"] //指定dex檔案目錄
            loader = ["com.handsome.thinker.AppLike.MyTinkerApplication"] //指定載入patch檔案時用到的類
        }
        lib {
            pattern = ["libs/*/*.so"] //指定so檔案目錄
        }
        res {
            pattern = ["res/*", "assets/*", "resources.arsc", "AndroidManifest.xml"] //指定資原始檔目錄
            ignoreChange = ["assets/sample_meta.txt"] //指定不受影響的資源路徑
            largeModSize = 100 //資源修改大小預設值
        }
        packageConfig {
            configField("patchMessage", "fix the 1.0 version's bugs")
            configField("patchVersion", "1.0")
        }
    }

    /**
     * 是否配置了多渠道
     */
    List<String> flavors = new ArrayList<>();
    project.android.productFlavors.each { flavor ->
        flavors.add(flavor.name)
    }
    boolean hasFlavors = flavors.size() > 0

    /**
     * 複製apk包和其它必須檔案到指定目錄
     */
    android.applicationVariants.all { variant ->
        /**
         * task type, you want to bak
         */
        def taskName = variant.name
        def date = new Date().format("yyyy-MM-dd-HH-mm-ss")
        tasks.all {
            if ("assemble${taskName.capitalize()}".equalsIgnoreCase(it.name)) {
                it.doLast {
                    copy {
                        def fileNamePrefix = "${project.name}-${variant.baseName}"
                        def newFileNamePrefix = hasFlavors ? "${fileNamePrefix}" : "${fileNamePrefix}-${date}"
                        def destPath = hasFlavors ? file("${bakPath}/${project.name}-${date}/${variant.flavorName}") : bakPath
                        from variant.outputs.outputFile
                        into destPath
                        rename { String fileName ->
                            fileName.replace("${fileNamePrefix}.apk", "${newFileNamePrefix}.apk")
                        }

                        from "${buildDir}/outputs/mapping/${variant.dirName}/mapping.txt"
                        into destPath
                        rename { String fileName ->
                            fileName.replace("mapping.txt", "${newFileNamePrefix}-mapping.txt")
                        }

                        from "${buildDir}/intermediates/symbols/${variant.dirName}/R.txt"
                        into destPath
                        rename { String fileName ->
                            fileName.replace("R.txt", "${newFileNamePrefix}-R.txt")
                        }
                    }
                }
            }
        }
    }
}

4、記得開啟Manifest許可權,否則生成拆分包的時候有奇怪錯誤

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

Tinker封裝

我們提供兩個方法來初始化Tinker

  • 預設的方式
  • 自定義模組的方式
public class TinkerManager {

    private static boolean isInstalled = false;
    // 這裡的ApplicationLike可以理解為Application的載體
    private static ApplicationLike mAppLike;
    private static CustomPatchListener mPatchListener;

    /**
     * 預設初始化Tinker
     *
     * @param applicationLike
     */
    public static void installTinker(ApplicationLike applicationLike) {
        mAppLike = applicationLike;
        if (isInstalled) {
            return;
        }

        TinkerInstaller.install(mAppLike);
        isInstalled = true;
    }

    /**
     * 初始化Tinker,帶有自定義模組
     * <p>
     * 1、CustomPatchListener
     * 2、CustomResultService
     *
     * @param applicationLike
     * @param md5Value        伺服器下發的md5
     */
    public static void installTinker(ApplicationLike applicationLike, String md5Value) {
        mAppLike = applicationLike;
        if (isInstalled) {
            return;
        }

        mPatchListener = new CustomPatchListener(getApplicationContext());
        mPatchListener.setCurrentMD5(md5Value);

        // Load補丁包時候的監聽
        LoadReporter loadReporter = new DefaultLoadReporter(getApplicationContext());
        // 補丁包載入時候的監聽
        PatchReporter patchReporter = new DefaultPatchReporter(getApplicationContext());
        AbstractPatch upgradePatchProcessor = new UpgradePatch();
        TinkerInstaller.install(applicationLike,
                loadReporter,
                patchReporter,
                mPatchListener,
                CustomResultService.class,
                upgradePatchProcessor);
        isInstalled = true;
    }

    /**
     * 增加補丁包
     *
     * @param path
     */
    public static void addPatch(String path) {
        if (Tinker.isTinkerInstalled()) {
            TinkerInstaller.onReceiveUpgradePatch(getApplicationContext(), path);
        }
    }

    /**
     * 獲取上下文
     *
     * @return
     */
    private static Context getApplicationContext() {
        if (mAppLike != null) {
            return mAppLike.getApplication().getApplicationContext();
        }
        return null;
    }
}

由於Tinker預設Patch檢查是沒有對檔案做Md5校驗,我們可以重寫其檢驗的方法,加上我們自己的檢驗邏輯(需要自定義模組的方式初始化Tinker)

CustomPatchListener.java

public class CustomPatchListener extends DefaultPatchListener {

    private String currentMD5;

    public void setCurrentMD5(String md5Value) {
        this.currentMD5 = md5Value;
    }

    public CustomPatchListener(Context context) {
        super(context);
    }

    /**
     * patch的檢測
     *
     * @param path
     * @return
     */
    @Override
    protected int patchCheck(String path) {
        //MD5校驗的工具可以網上查詢
        //這裡要求我們在初始化Tinker的時候加上MD5的引數
        //增加patch檔案的md5較驗
        if (!MD5Utils.isFileMD5Matched(path, currentMD5)) {
            return ShareConstants.ERROR_PATCH_DISABLE;
        }
        return super.patchCheck(path);
    }
}

由於Tinker預設安裝完補丁包之後是刪除補丁包,然後殺掉程序的方式,我們可以修改殺掉程序的行為(需要自定義模組的方式初始化Tinker)

CustomResultService.java

public class CustomResultService extends DefaultTinkerResultService {

    private static final String TAG = "Tinker.SampleResultService";

    /**
     * patch檔案的最終安裝結果,預設是安裝完成後殺掉自己程序
     * 此段程式碼主要是複製DefaultTinkerResultService的程式碼邏輯
     */
    @Override
    public void onPatchResult(PatchResult result) {
        if (result == null) {
            TinkerLog.e(TAG, "DefaultTinkerResultService received null result!!!!");
            return;
        }
        TinkerLog.i(TAG, "DefaultTinkerResultService received a result:%s ", result.toString());

        //first, we want to kill the recover process
        TinkerServiceInternals.killTinkerPatchServiceProcess(getApplicationContext());

        // if success and newPatch, it is nice to delete the raw file, and restart at once
        // only main process can load an upgrade patch!
        if (result.isSuccess) {
            //刪除patch包
            deleteRawPatchFile(new File(result.rawPatchFilePath));
            //殺掉自己程序,如果不需要則可以註釋,在這裡做自己的邏輯
            if (checkIfNeedKill(result)) {
                android.os.Process.killProcess(android.os.Process.myPid());
            } else {
                TinkerLog.i(TAG, "I have already install the newly patch version!");
            }
        }
    }
}

Tinker使用

1、Tinker的使用需要ApplicationLike來生成我們的Application,然後初始化Multidex和Tinker

@DefaultLifeCycle(application = ".MyTinkerApplication", flags = ShareConstants.TINKER_ENABLE_ALL, loadVerifyFlag = false)
public class CustomTinkerLike extends ApplicationLike {

    public CustomTinkerLike(Application application, int tinkerFlags, boolean tinkerLoadVerifyFlag, long applicationStartElapsedTime, long applicationStartMillisTime, Intent tinkerResultIntent) {
        super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime, applicationStartMillisTime, tinkerResultIntent);
    }

    @Override
    public void onBaseContextAttached(Context base) {
        super.onBaseContextAttached(base);

        MultiDex.install(base);
        TinkerManager.installTinker(this);
    }
}

2、編譯專案自動生成Application,然後在Manifest中指定我們的生成的Application

<application
    android:name=".AppLike.MyTinkerApplication"

3、在主頁面按鈕的點選事件,來載入放在快取目錄下的補丁包

public class MainActivity extends AppCompatActivity {

    private String mPath;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mPath = getExternalCacheDir().getAbsolutePath() + File.separatorChar;
    }

    /**
     * 載入Tinker補丁
     *
     * @param view
     */
    public void Fix(View view) {
        File patchFile = new File(mPath, "patch_signed.apk");
        if (patchFile.exists()) {
            TinkerManager.addPatch(patchFile.getAbsolutePath());
            Toast.makeText(this, "File Exists,Please wait a moment ", Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(this, "File No Exists", Toast.LENGTH_SHORT).show();
        }
    }
}

Tinker測試

完成Tinker的所有準備工作後,我們通過預設的初始化Tinker方式測試我們的補丁包

1、找到gradle工具欄,點選生成Release包,作為1.0版本的程式

這裡寫圖片描述

2、將生成的Release包Push到手機上,安裝,執行程式

生成apk的目錄在build的bakApk目錄下

這裡寫圖片描述

執行程式

這裡寫圖片描述

3、在專案中,對主介面新增載入圖片的按鈕,同時新增一個drawable檔案

public class MainActivity extends AppCompatActivity {

    private String mPath;
    private ImageView iv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        iv = (ImageView) findViewById(R.id.iv);

        mPath = getExternalCacheDir().getAbsolutePath() + File.separatorChar;
    }

    /**
     * 載入Tinker補丁
     *
     * @param view
     */
    public void Fix(View view) {
        File patchFile = new File(mPath, "patch_signed.apk");
        if (patchFile.exists()) {
            TinkerManager.addPatch(patchFile.getAbsolutePath());
            Toast.makeText(this, "File Exists,Please wait a moment ", Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(this, "File No Exists", Toast.LENGTH_SHORT).show();
        }
    }

    /**
     * 新增的按鈕點選事件
     *
     * @param view
     */
    public void Load(View view) {
        iv.setImageResource(R.drawable.bg_content_header);
    }

}

4、同時記得修改buildTinker.gradle的old安裝包的路徑,Tinker需要比對前後安裝包然後生成補丁包

//引數配置
ext {
    //開啟Tinker
    tinkerEnable = true
    //舊的apk位置,需要我們手動指定
    tinkerOldApkPath = "${bakPath}/app-release-2017-11-19-18-34-12.apk"
    //舊的混淆對映位置,如果開啟了混淆,則需要我們手動指定
    tinkerApplyMappingPath = "${bakPath}/"
    //舊的resource位置,需要我們手動指定
    tinkerApplyResourcePath = "${bakPath}/app-release-2017-11-19-18-34-12-R.txt"
    tinkerID = "1.0"
}

5、找到gradle工具欄,點選thinker生成Release補丁包,作為1.0版本的補丁

這裡寫圖片描述

6、將生成的Release補丁包Push到手機的快取目錄上,執行程式點選修復補丁包,稍等數秒程式會被殺掉,重啟點選載入圖片按鈕

生成的補丁包

這裡寫圖片描述

記得將補丁放到快取目錄下,修復補丁後的程式

這裡寫圖片描述

Tinker多渠道打包

1、Tinker支援多渠道打包,我們採用友盟的打包方式,下載友盟SDK,將jar包增加到專案上

這裡寫圖片描述

2、初始化友盟SDK(新版本的SDK似乎不用初始化了,找不到初始化入口)

3、Manifest增加友盟的AppKey配置和渠道配置

<meta-data
    android:name="UMENG_APPKEY"
    android:value="5a116bbea40fa33cf9000150" />
<meta-data
    android:name="UMENG_CHANNEL"
    android:value="${UMENG_CHANNEL_VALUE}" />

4、在app的build.gradle中增加多渠道打包資訊

/**
 * 配置多渠道
 */
productFlavors {
    googleplayer {
        manifestPlaceholders = [UMENG_CHANNEL_VALUE: "googleplayer"]
    }
    baidu {
        manifestPlaceholders = [UMENG_CHANNEL_VALUE: "baidu"]
    }
    wangdoujia {
        manifestPlaceholders = [UMENG_CHANNEL_VALUE: "wangdoujia"]
    }
    productFlavors.all {
        flavor -> flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]
    }
}

5、在buildTinker.gradle增加配置多渠道補丁包的生成規則

/**
 * 生成多渠道補丁包
 */
project.afterEvaluate {
    if (hasFlavors) {
        task(tinkerPatchAllFlavorRelease) {
            group = 'tinker'
            def originOldPath = getTinkerBuildFlavorDirectory()
            for (String flavor : flavors) {
                def tinkerTask = tasks.getByName("tinkerPatch${flavor.capitalize()}Release")
                dependsOn tinkerTask
                def preAssembleTask = tasks.getByName("process${flavor.capitalize()}ReleaseManifest")
                preAssembleTask.doFirst {
                    String flavorName = preAssembleTask.name.substring(7, 8).toLowerCase() + preAssembleTask.name.substring(8, preAssembleTask.name.length() - 15)
                    project.tinkerPatch.oldApk = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-release.apk"
                    project.tinkerPatch.buildConfig.applyMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-release-mapping.txt"
                    project.tinkerPatch.buildConfig.applyResourceMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-release-R.txt"
                }
            }
        }
        task(tinkerPatchAllFlavorDebug) {
            group = 'tinker'
            def originOldPath = getTinkerBuildFlavorDirectory()
            for (String flavor : flavors) {
                def tinkerTask = tasks.getByName("tinkerPatch${flavor.capitalize()}Debug")
                dependsOn tinkerTask
                def preAssembleTask = tasks.getByName("process${flavor.capitalize()}DebugManifest")
                preAssembleTask.doFirst {
                    String flavorName = preAssembleTask.name.substring(7, 8).toLowerCase() + preAssembleTask.name.substring(8, preAssembleTask.name.length() - 13)
                    project.tinkerPatch.oldApk = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-debug.apk"
                    project.tinkerPatch.buildConfig.applyMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-debug-mapping.txt"
                    project.tinkerPatch.buildConfig.applyResourceMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-debug-R.txt"
                }

            }
        }
    }
}

6、找到gradle工具欄,點選生成Release包,作為1.0版本的程式

這裡寫圖片描述

7、同時記得修改buildTinker.gradle的old安裝包的路徑,Tinker需要比對前後安裝包然後生成補丁包

//引數配置
ext {
    //開啟Tinker
    tinkerEnable = true
    //舊的apk位置,需要我們手動指定
    tinkerOldApkPath = "${bakPath}/app-2017-11-19-20-35-23"
    //舊的混淆對映位置,如果開啟了混淆,則需要我們手動指定
    tinkerApplyMappingPath = "${bakPath}/app-2017-11-19-20-35-23"
    //舊的resource位置,需要我們手動指定
    tinkerApplyResourcePath = "${bakPath}/app-2017-11-19-20-35-23"
    //舊的多渠道位置,需要我們手動指定
    tinkerBuildFlavorDirectory = "${bakPath}/app-2017-11-19-20-35-23"
    tinkerID = "1.0"
}

8、找到gradle工具欄,點選thinker生成Release補丁包,作為1.0版本的補丁

這裡對程式的修改就省略了

這裡寫圖片描述

後面的測試更上面一樣,也就省略了

原始碼下載

原始碼下載

結語

當野心大於現實的能力,只能默默的學習提升自己的能力,互相努力吧

相關推薦

Android實戰——Tinker整合使用

前言 對於熱修復我相信很多小夥伴都已經知道它們普遍的操作套路,Tinker主要是依賴自己的gradlePlugin生成拆分包,所以其拆分包的生成就由Gradle來完成,當然也可以通過命令列的方式,這裡就不對命令列做講解,Tinker接入指南 專案結構

Android實戰——輕鬆整合百度自動更新SDK,只需3步

輕鬆整合百度自動更新SDK,只需3步 整合百度自動更新SDK前提: 1、需要上線作品進行測試,本人用自己上線的作品進行了測試。 2、需要上線的作品defaultConfig中的versionCode

Android實戰簡易教程-第六十六槍(server端搭建server端Json數據交互)

視頻 pack tis sta listen let Coding read ide 學習Android有一段時間了。對server端有非常深的好奇,決定對serve

Android實戰——Retrofit2的使用封裝

請求 註解 請求頭 and retrofit 內容 部分 targe json 使用項目的原話:Android和Java中類型安全的HTTP客戶端 項目地址:https://github.com/square/retrofit 這裏Retrofit還需要導入它的Gson依賴

Android實戰——第三方服務之Bmob後端雲的推送服務的集成使用(三)

第一篇 文章 href 第三方服務 log 集成 android實戰 https 分享 第三方服務之Bmob後端雲的推送服務的集成和使用(三) 事先說明:這裏的一切操作都是在集成了BmobSDK之後實現的,如果對Bmob還不了解的話,請關註我第一篇Bmob文章 步驟

cordova整合sencha touch建立APP專案,以及Android Studio專案匯入打包

-------------------目錄結構-------------------------------------------         \app                                                          

Android app快速整合Mob shareSDK分享到微信QQ

  Android app整合Mob shareSDK分享到微信和QQ   線上安裝方法介紹   注意需要用到的appKey和appSecret 請自行到http://www.mob.com官網申請,這裡不介紹申請過程了   進入如

【Ionic實戰】一個AngularJS的跨平臺(iOS,Android) APP框架

關於 使用HTML5和CSS來開發手機應用,一直是廣大前端開發者的理想,並且已經有不少解決方案了。例如 PhoneGap(用javascript來呼叫裝置原生API)JQuery Mobile(UI庫)Titanium(混合方式)AppCan(國產的開發工具) Ioni

React Native for Android 實戰(一):配置起步

原文地址: http://www.csdn.net/article/2015-09-24/2825787-react-native Facebook開源React Native也勢要統一移動端程式語言,而其提前釋出React Native for Android更是引

Android 融雲IMKit的整合使用

1.整合 從官網下載SDK,這裡以Rong_Cloud_Android_IMKit_SDK_v2_8_7_Stable_8d65c為例 首先匯入IMKit和IMLib(IMKit以IMLib為基礎) Rong_Cloud_Android_IMKit_SDK_v2_8_7_S

關於android微信支付 支付寶支付的整合

最近專案中要整合微信和支付寶支付 在沒有接觸支付之間 覺得還是比較難的 但真正去實現的時候還是比較簡單的 就是有不少坑要去踩過之後才知道 支付寶支付 : 對於支付寶支付 我個人理解還是比較簡單的 https://open.alipay.com/platform/home.h

Tinker整合多渠道打包

Tinker是什麼 Tinker是微信官方的Android熱補丁解決方案,它支援動態下發程式碼、So庫以及資源,讓應用能夠在不需要重新安裝的情況下實現更新。當然,你也可以使用Tinker來更新你的外掛。 它主要包括以下幾個部分: 1.gradle編譯外掛: tinke

Android微信支付整合踩過的坑

近公司需要微信支付,所以不得不去看看微信支付文件。但是你懂得,那文件寫的真帶勁,看不懂。我直接放棄,開始整合。但是調起微信支付的時候:結果碼為-1,心裡一驚,肯定哪裡錯了,就開始找坑。所以把自己解決的過程分享給大家,讓整合微信支付成為很容易的一件事。 2、我們需要的資源

Android實戰簡易教程-第六十六槍(伺服器端搭建伺服器端Json資料互動)

學習Android有一段時間了,對伺服器端有很深的好奇,決定對伺服器端的實現進行一些研究,這裡實現了一個簡單的小例子,用於獲取伺服器端的json資料,例子很簡單,適合初學者學習使用。伺服器端首先我們搭建伺服器端,伺服器端使用struct2架構,對該架構不熟悉的人可以花一點時間

Facebook Android整合開發

關鍵字:Facebook | Android studio 3.0 Facebook離線包如何整合?公司的環境決定了無法通過倉庫的方法進行整合,因此只能下載Facebook SDK進行整合。 本文講解了Android Studio 3.0中離線整合Faceb

Tinker整合步驟整合中所需要的問題

整合Tinker所需要的問題: 1,複製demo中的build.gradle 修改完成以後出現 Error:(9, 0) Could not get unknown property 'TINKER_VERSION' for object of type

Android Codec 整合 video Overlay

Codec整合和video overlay是現在FSL對android多媒體修改的所有東西,codec library以.so的形式放在prebuilt目錄下,沒有原始檔。而video overlay的實現主要是使用了FSL的ipu底層庫,將視訊資料直接傳送到硬體,由硬體

Android使用EaseUI整合環信3.0 設定頭像使用者名稱 三

整合環信看了很多文件,還是不會,從官網下載的demo,也不會用,後來才發現應該下載官網的sdk,裡面有個examples的資料夾,使用studio直接開啟裡面的ChatDemoUI3.0,就是一個可以執行的demo.執行的時候會報一個異常 Error:Ex

Android實戰技巧:為從右向左語言定義複雜字串,程式碼xml設定

程式碼方式,一般是放在一個Utils.java作為公共方法 /// add by xxx.zhou for ArabicRTL support 20141024 begin public static boolean isContainEG_I

Android Studio中git的整合使用

一、下載安裝git安裝到本地目錄 二、Android Studio的配置 1、android studio找到setting 右上角test配置目錄,git的安裝路徑,點選右上角test出現suc