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
三、打开Project Settings/Player/Android/Publishing Settings,自定义Gradle
四、根据三的路径打开这两个文件,使用下面内容替换掉最上面的那行注释。在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对象。
通过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(); } });