读一读

keytool -exportcert -alias androiddebugkeyname -keystore xxx\debug.keystore | openssl sha1 -binary | openssl base64



需要在AndroidManifest.xml的manifest节点中添加声明

<queries>
    <!-- TELEGRAM -->
    <package android:name="org.telegram.messenger" />
    <!-- TELEGRAM -->
    <package android:name="org.telegram.messenger.web" />
    <!-- Facebook -->
    <package android:name="com.facebook.katana" />
    <!-- Twitter -->
    <package android:name="com.twitter.android" />
    <!-- WhatsAPP -->
    <package android:name="com.whatsapp" />
    <!-- MESSENGER -->
    <package android:name="com.facebook.orca" />
    <!-- SNAPCHAT -->
    <package android:name="com.snapchat.android" />
    <!-- INSTAGRAM -->
    <package android:name="com.instagram.android" />
</queries>


这里使用的是unity2019.4.x 以上应该也适用

但是unity打包使用的gradle是比较低版本的,不支持这个,所以打包的时候会报错,需要升级一下Gradle到5.6.4及更高版本。

一、先下载指定版本的Gradle

二、修改打包使用的Gradle,打开Editor/Preferences/External Tool

image.png

三、打开Project Settings/Player/Android/Publishing Settings,自定义Gradle

image.png

四、根据三的路径打开这两个文件,使用下面内容替换掉最上面的那行注释。在lintOptions中添加checkReleaseBuilds false。删掉相关行"useProguard **PROGUARD_DEBUG**",准备弃用了,不删会打包失败。

buildscript {
    repositories {
        google()
        jcenter()
    }
    dependencies {
        // Must be Android Gradle Plugin 3.6.4 or later. For a list of
        // compatible Gradle versions refer to:
        // https://developer.android.com/studio/releases/gradle-plugin
        classpath 'com.android.tools.build:gradle:3.6.4'
    }
}

allprojects {
   repositories {
      google()
      jcenter()
      flatDir {
        dirs 'libs'
      }
   }
}



public static void OpenFacebookPublicPage( String page_id , String name)
{
   try {
      AndroidPlugin.instance.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("fb://page/" + page_id)));
   } catch (Exception e) {
      //网页打开公共主页
      AndroidPlugin.instance.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.facebook.com/" + name)));
   }
}



//判断包名是否存在
public static synchronized boolean isContainPackName( String packName) {
   boolean isContainPack = false;
   try {
      PackageManager packageManager = AndroidPlugin.instance.getPackageManager();
      PackageInfo info = packageManager.getPackageInfo(packName, PackageManager.GET_ACTIVITIES);
      if (info != null) {
         isContainPack = true;
      }
   } catch (PackageManager.NameNotFoundException e) {
      e.printStackTrace();
   }
   return isContainPack;
}

@SuppressLint("WrongConstant")
public static void FacebookMessageShareLink(String linkurl) {
   if(!isContainPackName("com.facebook.orca"))
   {
      Toast.makeText(AndroidPlugin.instance, "no message app", 1).show();
      return;
   }

   Intent sendIntent = new Intent();
   sendIntent.setAction(Intent.ACTION_SEND);
   sendIntent.putExtra(Intent.EXTRA_TEXT,linkurl);//linkurl 为需要分享的内容
   sendIntent.setType("text/plain");
   sendIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
   sendIntent.setPackage("com.facebook.orca");//为需要分享到的包名
   AndroidPlugin.instance.startActivity(sendIntent);
}



public static int SavePhoto(String filePath, String fileNmae) {
    if (ActivityCompat.checkSelfPermission(AndroidPlugin.instance, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            String[] mPermissionList = new String[]{
                    Manifest.permission.WRITE_EXTERNAL_STORAGE,
            };
            if (ActivityCompat.shouldShowRequestPermissionRationale(AndroidPlugin.instance, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                ActivityCompat.requestPermissions(instance, mPermissionList, SdkConst.req_code_permission_writestorage);
                return 2;//忽略
            }
            else
            {
                ActivityCompat.requestPermissions(instance, mPermissionList, SdkConst.req_code_permission_writestorage);
                return 0;
            }
        }
    }

    instance.runOnUiThread(new Runnable() {
        public void run() {
            Bitmap bitmap = BitmapFactory.decodeFile(filePath);

            File file = new File(Environment.getExternalStorageDirectory()
                    + "/Pictures", fileNmae);

            FileOutputStream fos = null;
            try {
                fos = new FileOutputStream(file);
            } catch (FileNotFoundException e) {
                // TODO Auto-generated catch block
                Log.w("unity", e.toString());
            }
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);

            try {
                fos.flush();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                Log.w("cat", e.toString());
            }
            try {
                fos.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                Log.w("cat", e.toString());
            }
            bitmap.recycle();//扫描保存的图片
            instance.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + Environment.getExternalStorageDirectory()
                    + "/Pictures/" + fileNmae)));

        }
    });

    return 1;
}



服务可以和多个活动绑定,多个服务绑定的服务都是同一个实例。服务的销魂必须满足两个条件,就是服务本身没有运行并且没有和活动绑定在一起。

一、创建一个服务

public class MyService extends Service {

    public class  DownLoadBinder extends Binder
    {
        public void startDownload()
        {
            Log.e("MyService","开始下载");
        }

        public int getProgress()
        {
            Log.e("MyService","获取进度");
            return 0;
        }
    }

    private  DownLoadBinder mBinder = new DownLoadBinder();

    public MyService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.e("MyService","创建");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        Log.e("MyService","开始时候调用");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e("MyService","销毁了");
    }
}


二、绑定活动和解绑活动,开启服务和停止服务

private MyService.DownLoadBinder downLoadBinder;

private ServiceConnection connection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
        //iBinder为Service的onBind()返回的,用于服务和活动通信
        downLoadBinder = (MyService.DownLoadBinder) iBinder;
        downLoadBinder.startDownload();
        downLoadBinder.getProgress();
    }

    @Override
    public void onServiceDisconnected(ComponentName componentName) {
        Log.e("chicai","断开链接");
    }
};

@Override
protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
    setContentView((R.layout.first_layout));

    Button button15 = (Button)findViewById(R.id.button15);
    button15.setOnClickListener(new View.OnClickListener(){
        @Override
        public void onClick(View view) {
            Intent bindIntent = new Intent(Main2Activity.this,MyService.class);
            bindService(bindIntent,connection,BIND_AUTO_CREATE);//绑定活动
            startService(bindIntent);//开启服务,调用onStartCommand
        }
    });

    Button button14 = (Button)findViewById(R.id.button14);
    button14.setOnClickListener(new View.OnClickListener(){
        @Override
        public void onClick(View view) {
            unbindService(connection);//解绑活动
            Intent stopIntent = new Intent(Main2Activity.this,MyService.class);
            stopService(stopIntent);//停止服务
        }
    });
}

TextView text;
public static final int UPDATE_TEXT = 1;
private Handler handle = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what)
        {
            case UPDATE_TEXT:
                text.setText((String)msg.obj);
                break;
        }
    }
};

@Override
protected void onCreate(Bundle savedInstanceState) {

    text =  (TextView)findViewById(R.id.textView3);
    Button button13 = (Button)findViewById(R.id.button13);
    button13.setOnClickListener(new View.OnClickListener(){
        @Override
        public void onClick(View view) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    HttpURLConnection connection = null;
                    BufferedReader reader = null;
                    try{
                        URL url = new URL("http://www.chicai.group");
                        connection = (HttpURLConnection)url.openConnection();
                        connection.setRequestMethod("GET");
                        connection.setConnectTimeout(8000);
                        connection.setReadTimeout(8000);
                        connection.connect();
                        int responseCode = connection.getResponseCode();
                        if(responseCode != HttpURLConnection.HTTP_OK) return;
                        InputStream in = connection.getInputStream();
                        reader = new BufferedReader(new InputStreamReader(in));
                        String line;
                        StringBuilder response = new StringBuilder();
                        while((line = reader.readLine()) != null )
                        {
                            response.append(line);
                        }

                        //这里用Handle通知主线程修改UI
                        Message m = new Message();
                        m.what = UPDATE_TEXT;
                        m.obj = response.toString();
                        handle.sendMessage(m);
                    }
                    catch (Exception e)
                    {
                        e.printStackTrace();
                    }
                    finally {
                        if(reader != null)
                        {
                            try{
                                reader.close();
                            }catch (IOException e)
                            {
                                e.printStackTrace();
                            }
                        }
                        if(connection != null)
                            connection.disconnect();
                    }
                }
            }).start();
        }
    });
}


异步消息处理机制you4个部分组成:Message、Handler、MessageQueue和Looper。

Message是线程之间传递的消息,他可以在内部携带少量的信息,用于不同线程之间交换数据。使用what字段表示什么、arg1和arg2传递整形数值、obj传递一个对象。

Handler为处理者,主要用来发送和处理消息。发送消息一般是使用Handler的sendMessage()方法,发出的消息经过一系列辗转处理后,最终会传递到Handler的handleMessage()方法。

MessageQueue是消息队列的意思,他用来存放Handler发送的消息,这部分消息会一直存放在消息队列中,等待被处理。每个线程中只会有一个MessageQueue对象。

Looper是每个线程中MessageQueue的管家,调用Looper的loop()方法后,就会进入到一个无限循环当中,然后每当发现MessageQueue中存在一条消息,就会将它取出,并传递到Handler的handleMessage()方法中。每个线程中也只会有一个Looper对象。

图片.png


通过URL和HttpURLConnection发送网络协议获取内容,在通过InputStream和BufferedReader读取每一行内容,网络请求不能在主线程中发送,所以需要开启一个子线程来发送网络命令。然而在子线程中也不能访问UI内容,所以又需要回到主线程中显示内容。

@Override
protected void onCreate(Bundle savedInstanceState) {

    Button button13 = (Button)findViewById(R.id.button13);
    button13.setOnClickListener(new View.OnClickListener(){
        @Override
        public void onClick(View view) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    HttpURLConnection connection = null;
                    BufferedReader reader = null;
                    try{
                        URL url = new URL("http://www.chicai.group");
                        connection = (HttpURLConnection)url.openConnection();
                        connection.setRequestMethod("GET");
                        connection.setConnectTimeout(8000);
                        connection.setReadTimeout(8000);
                        connection.connect();
                        int responseCode = connection.getResponseCode();
                        if(responseCode != HttpURLConnection.HTTP_OK) return;
                        InputStream in = connection.getInputStream();
                        reader = new BufferedReader(new InputStreamReader(in));
                        String line;
                        StringBuilder response = new StringBuilder();
                        while((line = reader.readLine()) != null )
                        {
                            response.append(line);
                        }

                        ShowResponse(response.toString());
                    }
                    catch (Exception e)
                    {
                        e.printStackTrace();
                    }
                    finally {
                        if(reader != null)
                        {
                            try{
                                reader.close();
                            }catch (IOException e)
                            {
                                e.printStackTrace();
                            }
                        }
                        if(connection != null)
                            connection.disconnect();
                    }
                }
            }).start();
        }
    });
}

private void ShowResponse(final String str)
{
    runOnUiThread(new Runnable() {//异步消息处理机制的接口封装
        @Override
        public void run() {
            Toast.makeText(Main2Activity.this,str,Toast.LENGTH_LONG).show();
        }
    });
}

Android运行一些东西都是需要权限的,以前都会在AndroidManifest.xml里面事先声明好所有的权限,但是这样的话可能会让一些程序申请太多的权限,从而造成安全隐患。所以可以写成动态申请权限。

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView((R.layout.first_layout));
    
    Button button12 = (Button)findViewById(R.id.button12);
    button12.setOnClickListener(new View.OnClickListener(){
        @Override
        public void onClick(View view) {
            if(ContextCompat.checkSelfPermission(Main2Activity.this, Manifest.permission.CALL_PHONE)
                    != PackageManager.PERMISSION_GRANTED)//先检查权限
            {
                //检查是否可以弹出权限授权窗口,注意:第一次没弹过或点击了不再授权会返回false
                if (ActivityCompat.shouldShowRequestPermissionRationale(AndroidPlugin.instance, Manifest.permission.CALL_PHONE)) {
                    ActivityCompat.requestPermissions(instance, new String[]{Manifest.permission.CALL_PHONE}, SdkConst.CALL_PHONE);
                }
                else
                {
                    //因为第一次访问也是返回false,所以也需要请求授权,可以添加返回值区别,业务层 记录是否为第一次以决定是否弹出提示去设置开启权限
                    ActivityCompat.requestPermissions(instance, new String[]{Manifest.permission.CALL_PHONE}, SdkConst.CALL_PHONE);
                }
            }else
            {
                call();
            }
        }
    });
}

private void  call()
{
    try{
        Intent intent = new Intent(Intent.ACTION_CALL);
        intent.setData(Uri.parse("tel:10086"));
        startActivity(intent);
    }
    catch (SecurityException e)
    {
        Toast.makeText(Main2Activity.this,"没有权限",Toast.LENGTH_LONG).show();
        e.printStackTrace();
    }
}

@Override//申请权限回调
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    switch (requestCode)
    {
        case 1:
            if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
            {
                call();
            }else
            {
                Toast.makeText(Main2Activity.this,"没有权限" ,Toast.LENGTH_LONG).show();
            }
        break;
    }
}

SharedPreferences是使用键值对的方式来存储数据的。

一、存储

Button button10 = (Button)findViewById(R.id.button10);
button10.setOnClickListener(new View.OnClickListener(){
    @Override
    public void onClick(View view) {
        SharedPreferences.Editor editor = getSharedPreferences("data",MODE_PRIVATE).edit();
        editor.putString("name","tingting");
        editor.putInt("age",18);
        editor.apply();
    }
});


二、读取

Button button11 = (Button)findViewById(R.id.button11);
button11.setOnClickListener(new View.OnClickListener(){
    @Override
    public void onClick(View view) {
        SharedPreferences share = getSharedPreferences("data",MODE_PRIVATE);
        StringBuilder str = new StringBuilder();
        str.append(share.getString("name",""));
        str.append(share.getInt("age",100));
        str.append("了");
        Toast.makeText(Main2Activity.this,str.toString(),Toast.LENGTH_LONG).show();
    }
});