Android 基础功能例子

一、打开其他应用

打开acticity通过packageManager或componentName

主Activity
val intent: Intent = Intent(this, SecondActivity::class.java)
intent.putExtra("MainActivityData", "I am MainActivity")//以键值对的形式携带数据
startActivityForResult(intent, resquest_code)//resquest_code为从另一个Activity返回时的id

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    when (requestCode) {
        resquest_code -> if (resultCode == RESULT_OK) {
            val returnData = data?.getStringExtra("SecondActivityData")//打开的Activity关闭时携带的数据
            Log.d(TAG, "the return data is $returnData")
        }
    }
}

另一个Activity
	val mainActivityData = intent.getStringExtra("MainActivityData")//主Activity携带的数据
	Log.d(TAG, "the data is $mainActivityData")
    //返回
	val intent = Intent()
	intent.putExtra("SecondActivityData", "I am SecondActivity")//返回时传递的数据
	setResult(RESULT_OK, intent)
	finish()

打开另一个应用时可通过包名打开,则打开的是默认页面,也可通过包名+活动名打开特定的页面
pm list packages//当前设备下的所有包名,adb shell下
**pm list packages | grep ****//查找与关键字相匹配的包名,adb shell下
adb shell dumpsys window/activity | findstr mCurrentFocus//查找当前应用的包名或页面活动名
adb shell
logcat | grep 关键log词//刷选带有指定指令的log
adb shell am force-stop packageName //强制停止指定应用
adb logcat > logcat.txt //将日志输出到指定文件,若没有该文件则先创建

//通过包名
PackageManager packageManager = getPackageManager();
intent = packageManager.getLaunchIntentForPackage("com.example.tts");
if(intent != null ) { //如果没有默认打开的页面intent会为空
	startActivity(intent); 
}
//包名+活动名
//打开的活动页面需要其属性在AndroidManifest.xml中的Export = “true”;通过resolveActivity可判断
intent = new Intent();
ComponentName componentName = new ComponentName("包名","活动名");
intent.setComponent(componentName);
if(inetnt.resolveActivityInfo(getPackageManager(),PackagerManager.MATCH_DEFAULT_ONLY) !=null) {
	startActivity(intent);
}

二、定时器

1.Timer

//匿名内部类方式
   Timer().schedule(object : TimerTask() {
       override fun run() {
           Log.d("1233", "4566")
       }
   }, Date(), 1000)//每隔一秒执行一次,不传第二个参数则只执行一次,第二个参数设置具体时间则在该时间后开始每隔一秒执行一次

2.Handler

val handler = Handler()
mRunnable = object:Runnable {//可转化为Lambda表达式
    override fun run() {
        mChangeValue++
        mListener?.onChanged(mChangeValue)
        handler.postDelayed(mRunnable, 2000)//实现一直定时功能
    }
}
handler.postDelayed(mRunable,2000)

三、通知栏

val channel = NotificationChannel(id, name, NotificationManager.IMPORTANCE_DEFAULT)
//通知渠道,id作为发出通知时选择从哪个渠道发出通知,name为该渠道的名称,可以手动选择关闭或打开该渠道,第三个参数为优先级

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {//安卓8以上
     val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager//创建通知管理器
     val channelHigh = NotificationChannel("high", "High", NotificationManager.IMPORTANCE_HIGH)
     manager.createNotificationChannel(channelHigh)
 }
 
 val intent = Intent(this, NotificationActivity::class.java)//打开通知后跳转的详情页面
 val pendingIntent = PendingIntent.getActivity(this, 0, intent, FLAG_MUTABLE)//通知的点按操作响应
 val notification = NotificationCompat.Builder(this, "high")
     .setContentTitle("Title")
     .setContentText("content text")
     .setContentIntent(pendingIntent)
     .setPriority(NotificationCompat.PRIORITY_HIGH)
     .setSmallIcon(R.mipmap.ic_launcher)
     .setAutoCancel(true)//点击后自动移除通知
     .build()
 manager.notify(1, notification)//通知的唯一id,当有相同id的通知发出时后者会覆盖前者

添加点击按钮(功能:打开某个app)

val intent = Intent(Intent.ACTION_MAIN)
intent.component = ComponentName("pkg","cls")
val action = NotificationCompat.Action.Builder(
    R.mipmap.ic_launcher, "按钮名字", 
    PendingIntent.getActivity(this, 0, intent,PendingIntent.FLAG_MUTABLE)
).build()
notification.addAction(action)
manager.notify(1, notification)

添加点击按钮(发送信息——广播方式)

 val intent = Intent(context, ExecutionReceiver::class.java)//某个广播类
 intent.putExtra("content", content)//携带的数据
 val action = NotificationCompat.Action.Builder(
     R.mipmap.ic_launcher, "按钮名字",
     PendingIntent.getBroadcast(context, codeId, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE)
 ).build()
notification.addAction(action)
manager.notify(1, notification)

四、权限申请

1.权限申请

1.在AndrIodManifest.xml中申请,例如:
2.运行时申请:

if (ContextCompat.checkSelfPermission(this,Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
	ActivityCompat.requestPermissions(this,arrayOf(Manifest.permission.CALL_PHONE), 1)
} else {
	call()//已申请权限了执行的操作
}

override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
	super.onRequestPermissionsResult(requestCode, permissions, grantResults)
	when (requestCode) {
	1 -> {
	if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
		call()
	} else {
		Toast.makeText(this, "You denied the permission",Toast.LENGTH_SHORT).show()
	}
}

先判断用户是不是已经给过我们授权了,借助ContextCompat.checkSelfPermission()方法。checkSelfPermission()方法接收两个参数:第一个参数是Context;第二个参数是具体的权限名。
ActivityCompat.requestPermissions()方法向用户申请授权。requestPermissions()方法接收3个参数:第一个参数要求是Activity的实例;第二个参数是一个String数组,我们把要申请的权限名放在数组中即可;第三个参数是请求码,只要是唯一值就可以了。

2.打开某个权限申请页面

检查某个权限是否授权,未授权则打开权限授予页面
打开该应用的权限申请页面,设置应用的uri,通过该应用的包名

if (ContextCompat.checkSelfPermission(this,"某个权限") != PackageManager.PERMISSION_GRANTED) {
      val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
      val packageName = packageName
      val uri = Uri.fromParts("package", packageName, null)
      intent.data = uri
      startActivity(intent)
    } catch (e: Exception) {
        e.printStackTrace()
    }
}

打开某个权限的所有应用的申请页面需要先知道某个权限的设置常量,通过设置常量打开相应页面

if (ContextCompat.checkSelfPermission(
        this,
        "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
    ) != PackageManager.PERMISSION_GRANTED
) {
    try {
        val intent = Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS)
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        startActivity(intent)
    } catch (e: Exception) {
        e.printStackTrace()
    }
}

五、BroadcastReceiver

Android 广播主要分为标准广播和有序广播。标准广播属于异步式,在广播发出之后所有的接收器几乎在同一时刻接收,无法截断;有序广播属于同步式,前面的接收器可以截断;注册方式又分为动态注册和静态注册。

1.动态注册

动态注册即在代码中注册,必须在代码中手动注册和注销

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
		val intentFilter = IntentFilter()
		intentFilter.addAction("android.intent.action.TIME_TICK")//添加待注册的广播类型,每分钟
		timeChangeReceiver = TimeChangeReceiver()
		registerReceiver(timeChangeReceiver, intentFilter)//receiver,intentFilter
	}
	override fun onDestroy() {
        super.onDestroy()
        unregisterReceiver(timeChangeReceiver)//注销
    }
	//内部类,继承BroadcastReceiver类,重写onReceive
    inner class TimeChangeReceiver : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            Toast.makeText(context, "Time is changed", Toast.LENGTH_SHORT).show()
        }
    }
}

2.静态注册

创建一个Broadcast Receive组件(包名-new-other-Broadcast Receiver),在AndroidManifest.xml中receiver组件中增减一个组件,并在里面声明相应的action(广播);在receiver类的onReceive中执行相应逻辑即可。

  <receiver
      android:name=".MyBroadcastReceiver"
      android:enabled="true"
      android:exported="true" >
      <intent-filter>
          <action android:name="android.intent.action.BOOT_COMPLETED" />//该广播需要添加权限
      </intent-filter>
  </receiver>

3.自定义广播

1.创建一个Boradcast receiver组件,并声明自定义广播的action,在onReceive里执行相应方法;

AndroidManifest.xml
<receiver
    android:name=".MyBroadcastReceiver"
    android:enabled="true"
    android:exported="true" >
    <intent-filter android:priority="100">
        <action android:name="com.example.broadcast.MY_BROADCAST" />
    </intent-filter>
</receiver>

class MyBroadcastReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        // This method is called when the BroadcastReceiver is receiving an Intent broadcast.
        Toast.makeText(context, "receive my Boradcast", Toast.LENGTH_LONG).show()
    }
}

2.在另一个地方发送该自定义广播:

	val intent = Intent("com.example.broadcast.MY_BROADCAST")
	intent.setPackage(getPacgageName())
	sendBroadcast(intent)//标准广播,有序广播——sendOrderedBroadcast(intent,null)

	//有序广播:abortBroadcast(),截断、后面优先级低的则无法收到广播

六、ContentProvider

1.在AndoridManifest.xml文件中声明ContentProvider的唯一标识符

<provider 
	android:name="MyProvider"
	android:authorities="com.test.myprovider"/>//唯一标识符

设置Uri 
Uri uri = Uri.parse("content://com.test.myprovider/User/1")

在这里插入图片描述

2.ContentProvider的主要方法

// 外部进程向 ContentProvider 中添加数据
public Uri insert(Uri uri, ContentValues values) 

// 外部进程 删除 ContentProvider 中的数据  
public int delete(Uri uri, String selection, String[] selectionArgs) 
  
// 外部进程更新 ContentProvider 中的数据
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
  
// 外部应用 获取 ContentProvider 中的数据
uri:要查询的数据的URI。
projection:要查询的列的列表。
selection:查询的过滤条件。
selectionArgs:过滤条件中占位符的具体值。
sortOrder:查询结果的排序方式。
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,  String sortOrder)  

// ContentProvider创建后 或 打开系统后其它进程第一次访问该ContentProvider时 由系统进行调用
public boolean onCreate() 

// 得到数据类型,即返回当前 Url 所代表数据的MIME类型
public String getType(Uri uri)

3.ContentResolver

统一管理不同 ContentProvider间的操作,通过 URI 即可操作 不同的ContentProvider 中的数据
外部进程通过 ContentResolver类 从而与ContentProvider类进行交互

ContentResolver resolver =  getContentResolver(); 
Uri uri = Uri.parse("content://com.test.myprovider/user"); 
Cursor cursor = resolver.query(uri, null, null, null, "userid desc"); 

4.辅助工具类

ContentUris:

// withAppendedId():向URI追加一个id
Uri uri = Uri.parse("content://com.test.myprovider/user") 
Uri resultUri = ContentUris.withAppendedId(uri, 5);  // Uri:content://com.test.myprovider/user/5

// parseId():从URL中获取ID
Uri uri = Uri.parse("content://com.test.myprovider/user/5") 
long personid = ContentUris.parseId(uri); 
//获取的结果为:5

UriMatcher:

// 初始化时不匹配任何东西
UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH); 
    
// 在ContentProvider 中注册URI(addURI())
int URI_CODE_a = 1int URI_CODE_b = 2;
matcher.addURI("com.test.myprovider", "user1", URI_CODE_a); 
matcher.addURI("com.test.myprovider", "user2", URI_CODE_b); 
// 若URI资源路径 = content://com.test.myprovider/user1 ,则返回注册码URI_CODE_a
// 若URI资源路径 = content://com.test.myprovider/user2 ,则返回注册码URI_CODE_b

// 根据URI 匹配 URI_CODE,从而匹配ContentProvider中相应的资源(match())
@Override   
public String getType(Uri uri) {   
	Uri uri = Uri.parse(" content://com.test.myprovider/user1");   

	switch(matcher.match(uri)){   
		case URI_CODE_a:   
			return tableNameUser1;   
		case URI_CODE_b:   
			return tableNameUser2;
	}   
}

ContentObserver:

// 注册内容观察者ContentObserver
getContentResolver().registerContentObserver(uri);// 通过ContentResolver类进行注册,并指定需要观察的URI

// 当该URI的ContentProvider数据发生变化时,通知外界(即访问该ContentProvider数据的访问者)
public class UserContentProvider extends ContentProvider { 
	public Uri insert(Uri uri, ContentValues values) { 
		db.insert("user", "userid", values); 
		getContext().getContentResolver().notifyChange(uri, null); // 通知访问者
	} 
}

// 解除观察者
getContentResolver().unregisterContentObserver(uri);

示例:

public class MyProvider extends ContentProvider {
	public static final String AUTOHORITY = "com.test.myprovider";
	public static final int User_Code = 1;
    public static final int Job_Code = 2;

    // UriMatcher类使用:在ContentProvider 中注册URI
    private static final UriMatcher mMatcher;
    static{
        mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        mMatcher.addURI(AUTOHORITY,"user", User_Code);
        mMatcher.addURI(AUTOHORITY, "job", Job_Code);
    }
    //重载ContentProvider的几个方法 onCreate,insert,query等
}

// 设置URI
Uri uri_user = Uri.parse("content://com.test.myprovider/user");

// 插入表中数据
ContentValues values = new ContentValues();
values.put("id", 001);
values.put("name", "jack");

// 获取ContentResolver
ContentResolver resolver =  getContentResolver();
// 通过ContentResolver 根据URI 向ContentProvider中插入数据
resolver.insert(uri_user,values);

七、异步消息机制

1.Handler

Android中的异步消息处理主要由4个部分组成:Message、Handler、MessageQueue和Looper。

  1. Message
    Message是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间传递数据。如Message的what字段,除此之外还可以使用arg1和arg2字段来携带一些整型数据,使用obj字段携带一个Object对象。
  2. Handler
    Handler主要是用于发送和处理消息的。发送消息一般使用Handler的sendMessage()方法、post()方法等,接收消息在Handler的handleMessage()函数中。
  3. MessageQueue
    MessageQueue主要用于存放所有通过Handler发送的消息。这部分消息会一直存在于消息队列中,等待被处理。每个线程中只会有一个MessageQueue对象。
  4. Looper
    Looper是每个线程中的MessageQueue的管家,调用Looper的loop()方法后,就会进入一个无限循环当中,然后每当发现MessageQueue中存在一条消息时,就会将它取出,并传递到Handler的handleMessage()方法中。每个线程中只会有一个Looper对象。

首先需要在主线程当中创建一个Handler对象,并重写handleMessage()方法,在子线程中通过创建message对象通过sendMessage发送出去,这条消息会被添加到MessageQueue的队列中等待被处理,而Looper则会一直尝试从MessageQueue中取出待处理消息,最后分发回Handler的handleMessage()方法中。

val workThread: HandlerThread = HandlerThread("EventHandler thread")//创建一个单独的线程,让handler工作在单独的线程
var innerEventHandler = object : Handler(workThread.looper) {
    override fun handleMessage(msg: Message) {//对接收的数据进行处理
        super.handleMessage(msg)
        val bundle: Bundle = msg.data
        val text = bundle.getString("text")
        val source = bundle.getString("source")
        val event = Event(text, source)//Event自定义的一个类
    }
}

private fun sendMessage() {
   val bundle = Bundle()
   bundle.putString("text", "text")
   bundle.putString("source", "source")
   val msg = Message.obtain()
   msg.data = bundle
   innerEventHandler.sendMessage(msg)
}

post方法:可执行UI操作
   handler = new Handler();
   // 模拟后台线程发送消息到主线程
   Thread thread = new Thread(new Runnable() {
       @Override
       public void run() {
           // 模拟耗时操作
           try {
               Thread.sleep(3000);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
           // 使用 post 方法将任务发送到主线程
           handler.post(new Runnable() {
               @Override
               public void run() {
                   // 在主线程中处理任务
                   textView.setText("Hello from background thread!");
               }
           });
       }
   });
   thread.start();


   Handler handler = new Handler(Looper.getMainLooper());
   handler.post(new Runnable() {
       @Override
       public void run() {
           Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
       }
   });

2.AsyncTask

AsyncTask背后的实现原理也是基于异步消息处理机制的,只是Android帮我们做了很好的封装而已

AsyncTask是一个抽象类,必须创建一个子类去继承它。在继承时可以为AsyncTask类指定3个泛型参数。
**Params:**在执行AsyncTask时需要传入的参数,可用于在后台任务中使用。
**Progress:**在后台任务执行时,如果需要在界面上显示当前的进度。
**Result:**当任务执行完毕后,如果需要对结果进行返回。

需要经常重写的几个方法(自动调用):

  1. onPreExecute()
    这个方法会在后台任务开始执行之前调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。
  2. doInBackground(Params…)
    这个方法中的所有代码都会在子线程中运行,在这里去处理所有的耗时任务。任务一旦完成,就可以通过return语句将任务的执行结果返回,如果AsyncTask的第三个泛型参数指定的是Unit,就可以不返回任务执行结果。可以调用publishProgress (Progress…)方法来更新ui界面。
  3. onProgressUpdate(Progress…)
    当在后台任务中调用了publishProgress(Progress…)方法后,onProgressUpdate (Progress…)方法就会很快被调用,该方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新
  4. onPostExecute(Result)
    当后台任务执行完毕并通过return语句进行返回时,这个方法就很快会被调用。返回的数据会作为参数传递到此方法中,可以利用返回的数据进行一些UI操作,比如说提醒任务执行的结果,以及关闭进度条对话框等。
class DownloadTask : AsyncTask<Unit, Int, Boolean>() {
	override fun onPreExecute() {
		//初始化ui界面
	}
	override fun doInBackground(vararg params: Unit?) = try {
		while (true) {
			val downloadPercent = doDownload() // 这是一个虚构的方法
			publishProgress(downloadPercent)//更新ui
			if (downloadPercent >= 100) {
				break
			}
		}
		true
	} catch (e: Exception) {
		false
	}
	override fun onProgressUpdate(vararg values: Int?) {
		//更新ui界面
	}
	override fun onPostExecute(result: Boolean) {
		progressDialog.dismiss()// 关闭进度对话框
		// 在这里提示下载结果
		if (result) {
			Toast.makeText(context, "Download succeeded", Toast.LENGTH_SHORT).show()
		} else {
			Toast.makeText(context, " Download failed", Toast.LENGTH_SHORT).show()
		}
	}
}

简单来说,使用AsyncTask的诀窍就是,在doInBackground()方法中执行具体的耗时任务,在onProgressUpdate()方法中进行UI操作,在onPostExecute()方法中执行一些任务的收尾工作。如果想要启动这个任务,只需编写以下代码即可:DownloadTask().execute()
也可以给execute()方法传入任意数量的参数,这些参数将会传递到DownloadTask的doInBackground()方法当中。

八、Android file

写文件——FileOutputStream openFileOutput
读文件——FileInputStream openFileInput
openFileOutput写文件时当文件不存在,Android自动创建。

1.write

通过BufferedWriter直接写入字符串

public void writeFile(String inputText) {
    FileOutputStream outputStream = null;
    BufferedWriter writer = null;
    try {
        outputStream = openFileOutput("location", Context.MODE_APPEND);
        writer = new BufferedWriter(new OutputStreamWriter(outputStream));
        writer.write(inputText);
    } catch (IOException e) {
        e.printStackTrace();
    }
    finally {
        try {
            if(writer != null) {
                writer.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

通过FileOutputStream也可直接写入内容此时要转成byte[]
MODE_APPEND:每次追加内容
MODE_PRIVATE:每次覆盖内容

outputStream = openFileOutput("location", Context.MODE_APPEND);
outputStream.write(inputText.getBytes());

2.read

public String readFile() {
    FileInputStream inputStream = null;
    BufferedReader reader = null;
    StringBuilder content = new StringBuilder();
    try {
        inputStream = openFileInput("location");
        reader = new BufferedReader(new InputStreamReader(inputStream));
        String text = "";
        while ((text = reader.readLine()) != null) {
            content.append(text);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if(reader != null) {
            try {
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    return content.toString();
}

九、SharedPreferences

写入:
val editor = getSharedPreferences("data", Context.MODE_PRIVATE).edit()
editor.putString("name", "Tom")
editor.putInt("age", 28)
editor.putBoolean("married", false)
editor.apply()

读取:如果sp中未存入相关值,则读出的是get时的默认值
val prefs = getSharedPreferences("data", Context.MODE_PRIVATE)
val name = prefs.getString("name", "")
val age = prefs.getInt("age", 0)
val married = prefs.getBoolean("married", false)

十、SQLite数据库

SQLiteOpenHelper抽象类:
构造方法四个参数:context,数据库名,自定义的Cursor(一般可传null),版本号
两个重要的方法:getReadableDatabase()和getWritableDatabase()都可创建或打开一个现有的数据库
先实例化对象,然后调用getReadableDatabase或者getWritableDatabase方法即可创建数据库

class MyDatabaseHelper(val context: Context, name: String, version: Int) : SQLiteOpenHelper(context, name, null, version) {
	private val createBook = "create table Book (" +
	" id integer primary key autoincrement," +
	"author text," +
	"price real," +
	"pages integer," +
	"name text)"
	override fun onCreate(db: SQLiteDatabase) {
		db.execSQL(createBook)
		Toast.makeText(context, "Create succeeded", Toast.LENGTH_SHORT).show()
	}
	override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
		db.execSQL("drop table if exists Book")
		onCreate(db)
	}
}
//创建
val dbHelper = MyDatabaseHelper(this, "BookStore.db", 2)
dbHelper.writableDatabase
//插入
val values1 = ContentValues().apply {
	put("name", "The Da Vinci Code")
	put("author", "Dan Brown")
	put("pages", 454)
	put("price", 16.96)
}
dbHelper.insert("Book", null, values1) // 插入第一条数据
//更新
val values = ContentValues()
values2.put("price", 10.99)
db.update("Book", values2, "name = ?", arrayOf("The Da Vinci Code"))
//查询
val cursor = db.query("Book", null, null, null, null, null, null)
if (cursor.moveToFirst()) {
	do {
		// 遍历Cursor对象,取出数据并打印
		val name = cursor.getString(cursor.getColumnIndex("name"))
		val author = cursor.getString(cursor.getColumnIndex("author"))
		val pages = cursor.getInt(cursor.getColumnIndex("pages"))
		val price = cursor.getDouble(cursor.getColumnIndex("price"))
	} while (cursor.moveToNext())
}
cursor.close()

十一、读取assests目录中文件

AssestsManager对象

1.context

//获取AssetManager对象
val assetManager = context.assets
//InputStream 流
val inputStream: InputStream = assetManager.open("filename.txt")
//BufferedReader逐行读取
val reader = BufferedReader(InputStreamReader(inputStream))
var line: String?
while (reader.readLine().also { line = it } != null) {
    // 处理每一行的内容
}
//关闭
reader.close()
inputStream.close()

2.resources

val assetManager = resources.assets
val inputStream: InputStream = assetManager.open("filename.txt")

3.kotlin扩展函数

val assetManager = context.assets
val inputStream: InputStream = assetManager.open("filename.txt")

fun InputStream.readTextAndClose(charset: Charset = Charsets.UTF_8): String {
	return this.bufferedReader(charset).use { it.readText() }
}
val text: String = inputStream.readTextAndClose()

获取xml数据

<?xml version="1.0" encoding="utf-8"?>
<config>
    <item key="api_url">https://api.example.com</item>
    <item key="api_key">YOUR_API_KEY</item>
</config>

Resources res = getResources();
XmlResourceParser parser = res.getXml(R.xml.config); // R.xml.config是XML文件的资源ID
String key = "";
String value = "";
try {
    while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
        if (parser.getEventType() == XmlPullParser.START_TAG && "item".equals(parser.getName())) {
            key = parser.getAttributeValue(null, "key");
            value = parser.nextText();
            // 在这里处理获取到的配置数据,例如打印到Logcat
            Log.d("ConfigData", key + ": " + value);
        }
        parser.next();
    }
    parser.close();
} catch (XmlPullParserException | IOException e) {
    e.printStackTrace();
}

十二、获取drawable里的图片

val photoDrawable = Context.getDrawable(R.drawable.test)//获取图为Drawable
val photoBitmap = Bitmap.createBitmap(100,100,Bitmap.Config.ARGB_8888)//转为Bitmap并设置固定大小
val canvas = Canvas(photoBitmap)
photoBitmap.setBounds(0,0,canvas.width,canvas.height)
photoBitmap.draw(canvas)

SpannableString

设置字体颜色

val spannableString = SpannableString("SpannableString测试")
val color = ForegroundColorSpan(Color.RED)
spannableString.setSpan(color,startIndex,endIndex,Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)

设置图片

//现有的bitmap
val spannableString = SpannableString("SpannableString测试")
val bitmapImage = ImageSpan(context,photoBitamp)
spannableString.span(bitmapImage,startIndex,endIndex,Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
//加载网络图片
val spannableString = SpannableString("SpannableString测试")
Glide.with(AppContext.context).asBitmap()
	.load(imagePath)//imagePath:一个图片的网络地址
	.override(200,200)//设置长宽
    .into(object : SimpleTarget<Bitmap>() {
        override fun onResourceReady(bitmap: Bitmap,transition: Transition<in Bitmap>?) {
            val imageSpan = ImageSpan(context, bitmap)
            spannableString.setSpan(imageSpan,startIndex,endIndex,Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
        }
    })

设置链接

val spannableString = SpannableString("SpannableString测试")
spannableString.setSpan(object : ClickableSpan() {
    override fun onClick(widget: View) {
        val intent = Intent(Intent.ACTION_VIEW,Uri.parse(addresses))//address:链接地址
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        startActivity(intent)
    }
}, startIndex, endIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
TextView.append(spannableString)//将链接显示到TextView中
TextView.movementMethod = LinkMovementMethod.getInstance()

十三、在子线程中使用Toast

runOnUiThread {
	Toast.makeText(context,text,Toast.LENGTH_LONG).show()
}

runOnUiThread(new Runnable() {
    @Override
    public void run() {
        Toast.makeText(getApplicationContext(), "Toast message", Toast.LENGTH_SHORT).show();
    }
});
val handler = Handler(Looper.getMainLooper())
handler.post {
    Toast.makeText(context,text,Toast.LENGTH_LONG).show()
}

// 创建 Handler 对象
Handler handler = new Handler(Looper.getMainLooper());
// 在主线程中显示 Toast
handler.post(new Runnable() {
    @Override
    public void run() {
        Toast.makeText(getApplicationContext(), "Toast message", Toast.LENGTH_SHORT).show();
    }
});

十四、一个布局文件中使用其他布局文件

//获取其他布局文件及布局文件中的一些控件
 val view = LayoutInflater.from(mContext).inflate(R.layout.custom_checkbox, null)
 val lottie = view.findViewById<LottieAnimationView>(R.id.lottie)
 val imageDone = view.findViewById<ImageView>(R.id.imageDone)
 val imageNext = view.findViewById<ImageView>(R.id.imageNext)
 val text = view.findViewById<TextView>(R.id.text)
 //添加到该布局文件中
linearLayout.addView(view)

十五、网络

权限:uses-permission android:name=“android.permission.INTERNET”

1.webView

webView.settings.javaScriptEnabled = true
webView.webViewClient = WebViewClient()
webView.loadUrl("https://www.baidu.com")

2.HttpURLConnection

//读取
thread {
    var connection: HttpURLConnection? = null
    try {
        val response = StringBuilder()
        val url = URL("https://www.baidu.com")
        connection = url.openConnection() as HttpURLConnection
        connection.connectTimeout = 8000
        connection.readTimeout = 8000
        val input = connection.inputStream
        // 下面对获取到的输入流进行读取
        val reader = BufferedReader(InputStreamReader(input))
        reader.use {
            reader.forEachLine {
                response.append(it)
            }
        }
        Log.d("TAG", "$response")
    } catch (e: Exception) {
        e.printStackTrace()
    } finally {
        connection?.disconnect()
    }
}
//写入
thread {
    var connection: HttpURLConnection? = null
    try {
        val url = URL("https://www.baidu.com")
        connection = url.openConnection() as HttpURLConnection
        connection.connectTimeout = 8000
        connection.readTimeout = 8000
        connection.requestMethod = "POST"
        val output = DataOutputStream(connection.outputStream)
        output.writeBytes("username=admin&password=123456")
    } catch (e: Exception) {
        e.printStackTrace()
    } finally {
        connection?.disconnect()
    }
}

3.OkHttp

dependencies {
	implementation 'com.squareup.okhttp3:okhttp:4.1.0'
}

thread {
    try {
        val client = OkHttpClient()
        val request = Request.Builder()
            .url("https://www.baidu.com")
            .build()
        val response = client.newCall(request).execute()
        val responseData = response.body?.string()
        if (responseData == null) {
            Log.d("TAG", "null null null")
        } else {
            Log.d("TAG", "$responseData")
        }

//            val requestBody = FormBody.Builder()
//                .add("username", "admin")
//                .add("password", "123456")
//                .build()
//            val request = Request.Builder()
//                .url("https://www.baidu.com")
//                .post(requestBody)
//                .build()

    } catch (e: Exception) {
        e.printStackTrace()
    }
}

4.Retrofit

dependencies {
	implementation 'com.squareup.retrofit2:retrofit:2.6.1'
	implementation 'com.squareup.retrofit2:converter-gson:2.6.1'
}

class App(val id: String, val name: String, val version: String)

interface AppService {
	@GET("get_data.json")
	fun getAppData(): Call<List<App>>
}

val retrofit = Retrofit.Builder()
					   .baseUrl("http://10.0.2.2/")
					   .addConverterFactory(GsonConverterFactory.create())
					   .build()
val appService = retrofit.create(AppService::class.java)
appService.getAppData().enqueue(object : Callback<List<App>> {
	override fun onResponse(call: Call<List<App>>, response: Response<List<App>>) {
		val list = response.body()
		if (list != null) {
			for (app in list) {
				Log.d("TAG", "id is ${app.id}")
				Log.d("TAG", "name is ${app.name}")
				Log.d("TAG", "version is ${app.version}")
			}
		}
	}
	override fun onFailure(call: Call<List<App>>, t: Throwable) {
		t.printStackTrace()
	}
}

一、相机

val contentValues = ContentValues()
val fileName = "IMG_${System.currentTimeMillis()}.jpg"//拍摄的照片的命名
contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, fileName)//名字
contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures")//照片存储的相对路径位置
val uri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)//相机
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri)
startActivity(intent)

二、对话框

//极简风格,仅做提示
   val dialog = AlertDialog.Builder(this)
   dialog.setMessage("test")
   dialog.show()
//带有选择的对话框
   val dialog = AlertDialog.Builder(this)
   dialog.setTitle("dialog")
   dialog.setTitle("test")
   dialog.setCancelable(false)//点击物理返回键对话框显示与否
   dialog.setPositiveButton("OK", object : DialogInterface.OnClickListener {
       override fun onClick(dialog: DialogInterface?, which: Int) {
           Log.d("MAIN", "OK")
       }
   })
   dialog.setNegativeButton("CANCEL", object : DialogInterface.OnClickListener {
       override fun onClick(dialog: DialogInterface?, which: Int) {
           Log.d("MAIN", "CANCEL")
       }
   })
   dialog.show()

三、蓝牙

获取蓝牙的一些状态,如打开,关闭,连接,断连等

添加权限 AndroidManifest.xml

    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>

蓝牙的状态主要是以广播的形式传递

    val intentFilter = IntentFilter()
    intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED)
    intentFilter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED)
    intentFilter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED)
    broadcastReceiver = BluetoothBroadcatsReceiver()
    registerReceiver(broadcastReceiver, intentFilter)
	
   inner class BluetoothBroadcatsReceiver : BroadcastReceiver() {
      override fun onReceive(context: Context?, intent: Intent?) {
          when (intent?.action) {
              BluetoothDevice.ACTION_ACL_CONNECTED -> {
                  Log.d("MAIN", "连接")
              }
              BluetoothDevice.ACTION_ACL_DISCONNECTED -> {
                  Log.d("MAIN", "断连")
              }
              BluetoothAdapter.ACTION_STATE_CHANGED -> when (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 0)) {
                  BluetoothAdapter.STATE_ON -> Toast.makeText(context, "蓝牙已打开", Toast.LENGTH_LONG).show()
                  BluetoothAdapter.STATE_OFF -> Toast.makeText(context, "蓝牙已断开", Toast.LENGTH_LONG).show()
              }
          }
      }
  }

//获取当前连接的设备
 fun getConnectedBtDevice(): List<Int> {
	 val deviceList = mutableListOf<Int>()
     val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()//获取蓝牙适配器
     val bondedDevices = bluetoothAdapter.bondedDevices//得到已匹配的蓝牙设备列表
     if (bondedDevices != null && bondedDevices.size > 0) {
         for (bondedDevice in bondedDevices) {
             try {
                 //使用反射调用被隐藏的方法
                 val isConnectedMethod =
                     BluetoothDevice::class.java.getDeclaredMethod(
                         "isConnected"
                     )
                 isConnectedMethod.isAccessible = true
                 val isConnected =
                     isConnectedMethod.invoke(bondedDevice) as Boolean
                 if (isConnected) {
                     deviceList.add(bondedDevice.type)
                 }
             } catch (e: NoSuchMethodException) {
                 e.printStackTrace()
             } catch (e: IllegalAccessException) {
                 e.printStackTrace()
             } catch (e: InvocationTargetException) {
                 e.printStackTrace()
             }
         }
     }
     return deviceList
 }

//获取蓝牙配置中一些协议的连接状态
val adapter = BluetoothAdapter.getDefaultAdapter()
val a2dp = adapter.getProfileConnectionState(BluetoothProfile.A2DP) == BluetoothAdapter.STATE_CONNECTED
val headset = adapter.getProfileConnectionState(BluetoothProfile.HEADSET) == BluetoothAdapter.STATE_CONNECTED

四、音频播放

本地文件或raw下的资源文件。如果是本地文件需要获取文件的绝对路径,如果文件处于data外部则需要申请所有文件的读写权限——ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION。

 mMediaPlayer = MediaPlayer()
 val file: AssetFileDescriptor = context.resources.openRawResourceFd(R.raw.stop)
 mMediaPlayer.setDataSource(file.fileDescriptor, file.startOffset, file.length)
 file.close()
 mMediaPlayer.prepare()//同步,prepareAsync()-异步
 mMediaPlayer.setOnCompletionListener(object : OnCompletionListener{//可转化为简洁的Lambda表达式
     override fun onCompletion(mp: MediaPlayer?) {
         Log.d("VOICE","播放完毕")
     }
 })
 //一些其他接口:
start()开始播放
stop()停止播放,停止后无法再播放,出发再进行相关设置
pause()暂停播放,暂停后再开始播放从原来的位置继续
isPlaying()是否正在播放

五、语音识别SpeechRecognizer

通过Android接口获取外部语音输入
1.AndroidManifest.xml

<queries>
    <intent>
        <action android:name="android.speech.RecognitionService" />
    </intent>
</queries>

2.查询本机是否支持SpeechRecognizer,在某些手机上可能不支持会返回flase,在手机上安装个语音识别服务软件即可(eg:讯飞语记),此时语音输入通过第三方软件输入

val speech_flag = SpeechRecognizer.isRecognitionAvailable(applicationContext)//speech_flag = true/false

3.开启语音输入功能

 private fun startSpeechRecognizer() {
     val intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH).apply {
         putExtra(
             RecognizerIntent.EXTRA_LANGUAGE_MODEL,
             RecognizerIntent.LANGUAGE_MODEL_FREE_FORM
         )
     }
     startActivityForResult(intent, SPEECH_REQUEST_CODE)
 }

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    if (requestCode == SPEECH_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
        val spokenText: String? =
            data?.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS).let { results ->
                results?.get(0)
            }
        // Do something with spokenText.
        if (spokenText != null) {
            Log.d(TAG, spokenText)
        }
    }
    super.onActivityResult(requestCode, resultCode, data)
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MVP(Model-View-Presenter)是一种软件架构模式,它将软件应用程序分为三个基本部分:模型(Model)、视图(View)和表示器(Presenter)。MVP模式的主要目的是实现UI和业务逻辑之间的分离,使得代码更加可维护和可扩展。 下面是一个简单的MVP模式实现的Android登录功能: 1. 创建一个LoginActivity类作为View层,它将负责展示登录页面,处理用户的输入和显示登录结果。 ``` public class LoginActivity extends AppCompatActivity implements LoginContract.View { private EditText mUserNameEditText; private EditText mPasswordEditText; private Button mLoginButton; private TextView mResultTextView; private LoginContract.Presenter mPresenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); mUserNameEditText = findViewById(R.id.user_name_edit_text); mPasswordEditText = findViewById(R.id.password_edit_text); mLoginButton = findViewById(R.id.login_button); mResultTextView = findViewById(R.id.result_text_view); mPresenter = new LoginPresenter(this); mLoginButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String userName = mUserNameEditText.getText().toString(); String password = mPasswordEditText.getText().toString(); mPresenter.login(userName, password); } }); } @Override public void showResult(String result) { mResultTextView.setText(result); } } ``` 2. 创建一个LoginPresenter类作为Presenter层,它将负责处理业务逻辑,调用Model层进行数据处理,并将结果返回给View层。 ``` public class LoginPresenter implements LoginContract.Presenter { private LoginContract.View mView; private LoginModel mModel; public LoginPresenter(LoginContract.View view) { mView = view; mModel = new LoginModel(); } @Override public void login(String userName, String password) { boolean result = mModel.checkUser(userName, password); if (result) { mView.showResult("登录成功"); } else { mView.showResult("登录失败"); } } } ``` 3. 创建一个LoginModel类作为Model层,它将负责进行数据处理,例如检查用户名和密码是否匹配。 ``` public class LoginModel { public boolean checkUser(String userName, String password) { // 进行用户名和密码的匹配,这里假设用户名为admin,密码为123456 return userName.equals("admin") && password.equals("123456"); } } ``` 4. 创建一个LoginContract接口,它定义了View层和Presenter层的接口方法。 ``` public interface LoginContract { interface View { void showResult(String result); } interface Presenter { void login(String userName, String password); } } ``` 通过以上步骤,我们就完成了一个简单的MVP模式实现的Android登录功能。在这个例子中,View层处理UI展示和用户输入,Presenter层处理业务逻辑,并调用Model层进行数据处理和返回结果,Model层仅处理数据。这种分层架构可以使代码更加清晰、易于维护和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值