读一读

点击Android的返回键就可以销毁当前的活动了。还有一个方法就是当前的活动中调用finish()方法。

Button button2 = (Button)findViewById(R.id.button2);
button2.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        finish();
    }
});

在res/layout下右键,新建一个Layout resource file文件命名为first_layout,点击打开文件,然后拖动一个Button到界面上。在左下角点击Text切换的源码。

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/button"
        android:layout_width="115dp"
        android:layout_height="67dp"
        android:text="Button"
        tools:layout_editor_absoluteX="135dp"
        tools:layout_editor_absoluteY="140dp" />

</android.support.constraint.ConstraintLayout>


这时候就添加了一个名字为button的按钮了,然后我们在活动中使用这个新建的视图,并且获取到这个按钮,为他添加一个监听事件。

package group.chicai.study;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class Main2Activity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //设置这个活动的视图为first_layout,其实是传入一个id到R.id上,随后通过这个获取元素
        setContentView((R.layout.first_layout));

        //通过R.id获取到按钮,返回为View,强制转换为Button
        Button button1 = (Button) findViewById(R.id.button);
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(Main2Activity.this,"一段提示文字",Toast.LENGTH_SHORT).show();
            }
        });
    }
}

Android中的日志工具是Log(android.util.Log),这个类提供了5个方法来打印日志。

  1. Log.v(),级别verbose,打印一些意义最小的日志。

  2. Log.d(),打印调试信息,对应级别debug。

  3. Log.i(),用户打印一些比较重要的数据,级别info。

  4. Log.w(),打印一些警告信息,级别warn。

  5. Log.e(),打印程序的错误信息,级别error。


这些方法都要传入两个参数,第一个参数为tag(标签名字,用来检索日志使用),第二个参数是msg就是需要打印的信息了。


打开app/src/main下的AndroidManifest.xml文件,可以看到一个Activity的存在,所有的活动不在AndroidManifest.xml注册是不可以使用的。

<activity android:name=".MainActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

这段代码表示对MainActivity这个活动进行注册,intent-filter里面的两行代码表示MainActivity是这个项目的主活动,在手机上点击应用图标,首先启动的就是这个活动。


public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);//设置视图,活动和视图分离
    }
    
}

MainActivity继承AppCompatActivity,这是一种向下兼容的Activity。MainActivity中有一个onCreate()方法,这个方法是一个活动被创建时必定要执行的方法。


主要流程是:调用统一下单接口获取到预付订单id,在构造参数调起微信支付,支付成功、支付失败、支付取消都会调起WXPayEntryActivity回调再回到Unity中。然后在Unity中判断状态做出相应的提示,若支付成功要向后台去查询结果。(微信文档流程

统一下单接口调用和调起微信支付的参数都需要后台去获取,所以测试的时候可以直接使用 地址 获取到json参数来调起微信支付。

在微信支付文档中的资源下载/Android资源下载中下载范例代码,工程里面有一个lib目录,里面有两个jar包(wechat-sdk-android-with-mta-1.0.2.jar和libammsdk.jar),把libammsdk.jar直接拖到Unity的Plugins/Android目录下,就可以使用com.tencent.mm.opensdk.openapi的类了。


Android调起微信支付:

[Serializable]
public class GetPrePayData
{
    public int status;
    public string appid;
    public string partnerid;
    public string package;
    public string noncestr;
    public string timestamp;
    public string prepayid;
    public string sign;
}

Dictionary<string, string> input = new Dictionary<string, string>();
public void WechatZhifu(string jsonData)
{
    AndroidJavaClass androidClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
    AndroidJavaObject androidObj = androidClass.GetStatic<AndroidJavaObject>("currentActivity");
    AndroidJavaClass apiFactory = new AndroidJavaClass("com.tencent.mm.opensdk.openapi.WXAPIFactory");
    //mWeixinPayApi保存引用后可以一直调用,createWXAPI方法第一个参数为Unity的活动Activity,第二个参数为微信支付的appid
    AndroidJavaObject mWeixinPayApi = apiFactory.CallStatic<AndroidJavaObject>("createWXAPI", androidObj, WxPayConfig.GetConfig().GetAppID());
    input.Clear();
    GetPrePayData data = new GetPrePayData();
    try
    {
        data = JsonUtility.FromJson<GetPrePayData>(jsonData);
    }
    catch (Exception e)
    {
        Debug.LogError("微信支付统一接口数据错误");
    }
    input.Add("appId", data.appid);
    input.Add("partnerId", data.partnerid);
    input.Add("prepayId", data.prepayid);
    input.Add("packageValue", data.package);
    input.Add("nonceStr", data.noncestr);
    input.Add("timeStamp", data.timestamp);
    AndroidJavaObject request = new AndroidJavaObject("com.tencent.mm.opensdk.modelpay.PayReq");
    foreach (var kv in input)
        request.Set<string>(kv.Key, kv.Value.ToString());//设置参数
    request.Set<string>("sign", data.sign);
    mWeixinPayApi.Call<bool>("sendReq", request);//调起微信支付
}


支付回调:要为应用包名创建一个wxapi/WXPayEntryActivity.java供微信调用回调方法。新建一个AndroidStudio工程,添加一个library模块,包名设置为和Unity项目应用的一样。将unity的jar包和示例文件中的libs的jar包添加到这个模块中。然后在示例文件中,复制wxapi/WXPayEntryActivity.java和Constants.java到模块/src/main/java/com...包名/下,注意修改Constants.java的APP_ID为你的应用ID,修改WXPayEntryActivity.java中包名为你应用包名,去掉视图,修改回调方法去通知Unity的某个物体的某个方法并将微信的返回代码返回给Unity,结束这个Activity就行了。

package com.zfzx.cyzx.wxapi;

import com.zfzx.cyzx.Constants;
import com.zfzx.cyzx.R;

import com.tencent.mm.opensdk.constants.ConstantsAPI;
import com.tencent.mm.opensdk.modelbase.BaseReq;
import com.tencent.mm.opensdk.modelbase.BaseResp;
import com.tencent.mm.opensdk.openapi.IWXAPI;
import com.tencent.mm.opensdk.openapi.IWXAPIEventHandler;
import com.tencent.mm.opensdk.openapi.WXAPIFactory;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;

import com.unity3d.player.UnityPlayer;

public class WXPayEntryActivity extends Activity implements IWXAPIEventHandler{
   
   private static final String TAG = "MicroMsg.SDKSample.WXPayEntryActivity";
   
    private IWXAPI api;
   
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //setContentView(R.layout.pay_result);
        
       api = WXAPIFactory.createWXAPI(this, Constants.APP_ID);
        api.handleIntent(getIntent(), this);
    }

   @Override
   protected void onNewIntent(Intent intent) {
      super.onNewIntent(intent);
      setIntent(intent);
        api.handleIntent(intent, this);
   }

   @Override
   public void onReq(BaseReq req) {
   }

   @Override
   public void onResp(BaseResp resp) {
      Log.d("jjjjjj", "onPayFinish, errCode = " + resp.errCode);
      UnityPlayer.UnitySendMessage("WechatPayRespGameObject","OnWechatPayResp",String.valueOf(resp.errCode));
      finish();
   }
}

把这个Android模块编译成jar包,然后扔进Unity的Plugins/Android目录下。随后在Unity中新建一个物体名称为WechatPayRespGameObject,任意挂上一个脚本,声明一个方法叫OnWechatPayResp接受调,然后根据返回代码做出相应的处理。

//Android的支付回调
private void OnWechatPayResp(string code)
{
    if (HallTransfer.Instance == null) return;
    switch (code)
    {
        case "0":
            HallTransfer.Instance.cnTipsBox("充值成功", 3);
            break;
        case "-1":
            HallTransfer.Instance.cnTipsBox("充值失败", 3);
            break;
        case "-2":
            HallTransfer.Instance.cnTipsBox("用户取消", 3);
            break;
    }
}


对了,还需要修改AndroidManifest.xml,添加一个Activity

<activity android:name=".wxapi.WXPayEntryActivity" android:exported="true"></activity>

.gradle和.idea是自动生成的一些文件,无须关系。app为项目中的代码和资源,开发基本是围绕这个目录开发。build包含编译时自动生成的文件,无须关心。gradle这个目录下包含了gradle warpper的配置文件,使用gradle wrapper的方式不需要提前将gradle下载好,而是会自动根据本地的缓存情况决定是否需要联网下载gradle。

app下的项目目录:build和上层的build类似。libs为项目放置使用到的第三方jar包。src/main/java放置所有Java代码的地方。src/main/res放置所有的图片、布局、字符串等资源,图片放到drawable目录下,布局放在layout目录下,字符串放在values目录下。AndroidManifest.xml是整个Android项目的配置文件,程序定义的四大组件都需要在这个文件里注册,可以为应用添加权责声明。


活动(Activity)是所有Android应用程序的门面,凡是在应用中你看到的东西都是放在活动中的。

服务(Service)会在后台默默地运行,即使用户退出了应用,服务仍然是可以继续运行的。

广播接收器(Broadcast Receiver)运行你的应用接收来自各处的广播消息,如电话短信等,当然也可以向外发出广播消息。

内容提供器(Content Provider)则为应用程序之间共享数据提供了可能,比如读取系统电话簿中的联系人。


//Java UpdateUtil.java
package com.cdx.unity3d;

import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.support.v4.content.FileProvider;
import java.io.File;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.os.Handler;
import android.os.Message;
import com.unity3d.player.UnityPlayer;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

public class UpdateUtil {

    private Context mContext;
    private String apkUrl;
    private String savePath;
    private String saveFileName;
    private String downloadMsg;
    private int progress;
    private ProgressDialog downloadDialog;
    private Thread downLoadThread;
    private boolean interceptFlag = false;

    public UpdateUtil(Context context)
    {
        this.mContext = context;
    }
    /**
     * 更新下载apk
     */
    public static void downLoadApk(final String url, final String storge, Boolean showNotify) {

        final Activity activity = UnityPlayer.currentActivity;
        activity.runOnUiThread(new Runnable()
        {
            public void run()
            {
                UpdateUtil updater = new UpdateUtil((Context)activity);
                updater.Download(url, storge);
            }
        });
    }

    public void Download(String url, String storge)
    {
        this.apkUrl = url;
        this.savePath = storge;

        this.saveFileName =  storge;

        this.downloadDialog = new ProgressDialog(this.mContext);
        this.downloadDialog.setTitle("游戏更新");
        this.downloadDialog.setMessage("请稍后。。。");
        this.downloadDialog.setProgressStyle(1);
        this.downloadDialog.setCancelable(false);
        this.downloadDialog.setCanceledOnTouchOutside(false);

        this.downloadDialog.setMax(100);
        this.downloadDialog.show();

        downloadApk();
    }

    private void downloadApk()
    {
        this.downLoadThread = new Thread(this.mdownApkRunnable);
        this.downLoadThread.start();
    }

    private Runnable mdownApkRunnable = new Runnable()
    {
        public void run()
        {
            try
            {
                URL url = new URL(UpdateUtil.this.apkUrl);

                HttpURLConnection conn = (HttpURLConnection)url.openConnection();
                conn.connect();
                int length = conn.getContentLength();
                InputStream is = conn.getInputStream();

                File file = new File(UpdateUtil.this.saveFileName);
                if (file.isFile()) {
                    file = new File(file.getParent());
                }
                if ((file.isDirectory()) && (!file.exists())) {
                    file.mkdir();
                }
                String apkFile = UpdateUtil.this.saveFileName;
                File ApkFile = new File(apkFile);
                FileOutputStream fos = new FileOutputStream(ApkFile);

                String fName = UpdateUtil.this.apkUrl.trim();
                fName = fName.substring(fName.lastIndexOf("/") + 1,fName.lastIndexOf("?"));


                UpdateUtil.this.downloadMsg = ("正在下载文件:" + fName);
                UpdateUtil.this.mHandler.sendEmptyMessage(0);

                int count = 0;
                byte[] buf = new byte[1024];
                do
                {
                    int numread = is.read(buf);
                    count += numread;
                    UpdateUtil.this.progress = ((int)((float)count / (float) length * 100.0F));
                    UpdateUtil.this.downloadMsg = ("正在下载文件:" + fName + "   " + count / 1024 + "kb/" + length / 1024 + "kb");

                    UpdateUtil.this.mHandler.sendEmptyMessage(1);
                    if (numread <= 0) {
                        break;
                    }
                    fos.write(buf, 0, numread);
                } while (!UpdateUtil.this.interceptFlag);
                fos.close();
                is.close();
                UpdateUtil.this.mHandler.sendEmptyMessage(2);
            }
            catch (MalformedURLException e)
            {
                e.printStackTrace();
                UpdateUtil.this.downloadMsg = ("下载失败,出现异常:" + e.getMessage());
                UpdateUtil.this.mHandler.sendEmptyMessage(3);
            }
            catch (IOException e)
            {
                e.printStackTrace();
                UpdateUtil.this.downloadMsg = ("下载失败,出现异常:" + e.getMessage());
                UpdateUtil.this.mHandler.sendEmptyMessage(3);
            }
        }
    };

    private Handler mHandler = new Handler()
    {
        public void handleMessage(Message msg)
        {
            switch (msg.what)
            {
                case 0:
                    UpdateUtil.this.downloadDialog.setMessage(UpdateUtil.this.downloadMsg);
                    break;
                case 1:
                    UpdateUtil.this.downloadDialog.setMessage(UpdateUtil.this.downloadMsg);
                    UpdateUtil.this.downloadDialog.setProgress(UpdateUtil.this.progress);
                    break;
                case 2:
                    UpdateUtil.this.installApk();

                    UpdateUtil.this.downloadDialog.dismiss();

                    break;
                case 3:
                    UpdateUtil.this.downloadDialog.dismiss();

                    AlertDialog.Builder builder = new AlertDialog.Builder(UpdateUtil.this.mContext);
                    builder.setTitle("下载失败");
                    builder.setMessage("是否重试?");
                    builder.setPositiveButton("确定", new DialogInterface.OnClickListener()
                    {
                        public void onClick(DialogInterface dialog, int which)
                        {
                            dialog.dismiss();
                            UpdateUtil.this.Download(UpdateUtil.this.apkUrl, UpdateUtil.this.savePath);
                        }
                    });
                    builder.setNegativeButton("取消", new DialogInterface.OnClickListener()
                    {
                        public void onClick(DialogInterface dialog, int which)
                        {
                            dialog.dismiss();

                            Activity act = (Activity)UpdateUtil.this.mContext;
                            act.finish();
                        }
                    });
                    builder.create().show();

                    break;
            }
        }
    };

    /**
     * 安装apk
     */
    private void installApk() {
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        Uri data = null;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {//判断版本大于等于7.0
            // 生成文件的uri,,
            // 注意 下面参数com.xxx.xxx.fileprovider为apk的包名加上.fileprovider,
            data = FileProvider.getUriForFile(this.mContext, "com.xxx.xxx.fileprovider", new File(this.saveFileName));
            intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);// 给目标应用一个临时授权
        } else {
            data = Uri.fromFile(new File(this.saveFileName));
        }
        intent.setDataAndType(data, "application/vnd.android.package-archive");


        this.mContext.startActivity(intent);
    }
}


修改Plugins/Android/AndroidManifest.xml文件,添加权限和provider

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

<provider
      android:name="android.support.v4.content.FileProvider"
      android:authorities="com.zfzx.cyzx.fileprovider"
      android:exported="false"
      android:grantUriPermissions="true">
  <meta-data
      android:name="android.support.FILE_PROVIDER_PATHS"
      android:resource="@xml/filepaths"/>
</provider>


在Plugins/Android/res/xml下添加filepaths.xml文件,注意替换包名!

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
  <!--<external-path name="external_files" path="."/>-->
  <external-path path="Android/data/包名!" name="files_root" />
  <external-path path="." name="external_storage_root" />
</paths>


这样把下载的apk的文件保存在Application.persistentDataPath目录下,也就是在downloadApk方法中传入的storge参数为Application.persistentDataPath + "/包.apk"就可以了。如果打包不了或报错了,将android-support-v4.jar放到Plugins/Android目录下。


一、新建一个Android Studio项目工程,设置最低的minimum SDK的为API 16:Android 4.1(Jelly Bean)

二、然后通过File->New->New Module,新建一个模块,选择Android Library,Package name如果需要继承UnityPlayerActivity则需要和项目的包名一致,如果只是使用classes.jar里面的一些方法,则自己选择一个符合的包名字,minimum SDK同样选择16

三、导入Unity的包,在Unity的安装目录中搜索classes.jar,找到它然后拖入到Project视图-》模块名字-》libs里面,然后右键classes.jar-》Add As Library..

四、在模块-》src->main->包,右键新建一个Java Class,取一个名字就是类名

package com.cdx.unity3d;

import android.content.Context;
import android.app.Activity;
import android.util.Log;

import com.unity3d.player.UnityPlayer;

public class Utils {

    /**
     * android utils log tag
     */
    private static final String TAG = "Utils";

    /**
     * android utils log message
     */
    public static void Log(String message) {
        Log.e(TAG, message);
    }

    /**
     * U3d程序上下文
     */
    private Context mContext;
    /**
     * U3d接收消息对象名称
     */
    private String mUnityObjName;

    /**
     * 与Unity3d传递消息
     */
    private void CallUnityMethod(String methodName, String message) {

        UnityPlayer.UnitySendMessage(mUnityObjName, methodName, message);
    }

    /**
     * 初始化工具
     */
    public void Initialize(Object context, String objName) {

        mContext = (Context) context;
        mUnityObjName = objName;
        Utils.Log("Utils Initialize Success");
    }

    /**
     * 上下文名称
     */
    public String GetContextName() {

        String name = mContext.toString();
        return name;
    }
    
    /**
     * 获取Url Scheme参数
     */
    public String GetSchemePars()
    {
        String data = SchemeUtil.GetSchemePars((Activity)mContext);
        return data;
    }
}


package com.cdx.unity3d;

import android.app.Activity;
import android.net.Uri;

public class SchemeUtil {

    public static String GetSchemePars(Activity activity)
    {
        String Param1 = "";
        String Param2 = "";
        String Param3 = "";
        String Param4 = "";
        String Param5 = "";
        String Param6 = "";
        Uri mLink = activity.getIntent().getData();
        if(mLink != null)
        {
            Param1 = mLink.getQueryParameter("Param1");
            Param2 = mLink.getQueryParameter("Param2");
            Param3 = mLink.getQueryParameter("Param3");
            Param4 = mLink.getQueryParameter("Param4");
            Param5 = mLink.getQueryParameter("Param5");
            Param6 = mLink.getQueryParameter("Param6");
        }

        String returnName = Param1 + "|" + Param2 + "|" + Param3 + "|" + Param4 + "|" + Param5 + "|" + Param6;

        activity.getIntent().setData(null);//获取一次后设为空,防止每次都能获取到

        return returnName;
    }
}


五、Unity上对应的工具脚本

using UnityEngine;
using System.Runtime.InteropServices;

namespace com.Bowen.Game.Lobby
{
    public class PluginManager : MonoBehaviour
    {

        void Start()
        {
            #if UNITY_ANDROID && !UNITY_EDITOR
                AndroidClassInitialize();
            #endif
        }

        private AndroidJavaObject mJavaObject { get; set; }

        //初始化,把Unity附体和Activity(Context)传递给Android写的初始化方法
        private void AndroidClassInitialize()
        {
            AndroidJavaClass androidClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
            AndroidJavaObject androidObj = androidClass.GetStatic<AndroidJavaObject>("currentActivity");
            mJavaObject = new AndroidJavaObject("com.cdx.unity3d.Utils");//包名+类名 工具类
            mJavaObject.Call("Initialize", androidObj, gameObject.name);//传参,保存应用,可以让Android那边通知Unity这边,也可以让那边访问上下文
        }

        #if UNITY_IOS && !UNITY_EDITOR
                [DllImport("__Internal")]
                public static extern string _GetURLString();
        #endif

        public string GetSchemePars()
        {
            string data = "";
            #if UNITY_ANDROID && !UNITY_EDITOR
                    data = mJavaObject.Call<string>("GetSchemePars");
            #elif UNITY_IOS && !UNITY_EDITOR
                    data = _GetURLString();
            #endif
            return data;
        }
    }

}


六、在Android Studio导出jar包,在Project中选择指定模块然后Build-》Make Module ‘模块名’,随后就可以在Project下你的模块下build有个outputs/arr,把里面的arr文件弄出来,把后缀改成zip,打开把里面的classes.jar拉出来,这个就是jar包了,改个名字,拉进Unity的Plugins/Android下,因为我这里写的只是工具类,所以AndroidManifest.xml不需要导入。

七、修改项目存在的AndroidManifest.xml,如果没有,那就把上面那个包的也拉进项目中。在主activity结点下加上

<intent-filter>
  <action android:name="android.intent.action.MAIN" />
  <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
	  
<intent-filter>  
  <action android:name="android.intent.action.VIEW"/>  
  <category android:name="android.intent.category.DEFAULT" />  
  <category android:name="android.intent.category.BROWSABLE" />  
  <data android:scheme="axb" android:host="love.app" android:pathPrefix="/openwith"/>  
</intent-filter>

八、打开的url就为 axb://love.app/openwith?Param1=1&Param2=2


今天有需求要复制微信号后打开微信,我写了个jar包导进工程中,然后调用我写的接口方法,居然屁反应都没有,后来发现,我写的方法都是使用Android自带的类库实现的,那么应该就可以直接在Unity中获取这些类和方法直接实现了,我百度了一下,就发现了轮子可。

void openPackage(string pkgName)
{
    using (AndroidJavaClass jcPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))//Unityd的主Active就是UnityPlayer
    {
        using (AndroidJavaObject joActivity = jcPlayer.GetStatic<AndroidJavaObject>("currentActivity"))//获取静态属性
        {
            using (AndroidJavaObject joPackageManager = joActivity.Call<AndroidJavaObject>("getPackageManager"))//获取PackageManager这个类
            {
                using (AndroidJavaObject joIntent = joPackageManager.Call<AndroidJavaObject>("getLaunchIntentForPackage", pkgName))//调用这个方法
                {
                    if (null != joIntent)
                    {
                        joActivity.Call("startActivity", joIntent);//主Active开启另外的Active
                    }
                }
            }
        }
    }
}


只要传入包名,就可以直接打开该包名的应用了。