December 8, 2024
导入
#
dependencies {
implementation 'com.squareup.retrofit2:retrofit:2.6.1'
implementation 'com.squareup.retrofit2:converter-gson:2.6.1'
}
第一个协程
#
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
getAppDataBtn.setOnClickListener {
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("MainActivity", "id is ${app.id}")
Log.d("MainActivity", "name is ${app.name}")
Log.d("MainActivity", "version is ${app.version}")
}
}
}
override fun onFailure(call: Call<List<App>>, t: Throwable) {
t.printStackTrace()
}
})
}
}
}
定义接口
#
interface AppService{
//静态
@GET("get_data.json")
fun getData(): Call<Data>
//动态参数
@GET("page/{page}/get_data.json")
fun getPageData(@Path("page") page:Int): Call<Data>
//get 带参数
@GET("user/login")
fun login(@Query("username") username:String,@Query("password") password:String): Call<ResponseBody>
//post
@POST("user/login")
fun login(@Body data:Data): Call<ResponseBody>
//指定静态header呢
@Header("sign: 123")
//动态Header
@POST("user/login")
fun login(@Header("sign") sign:String,@Body data:Data): Call<ResponseBody>
}
论Retrofit构建起的最佳写法
#
先写一个ServiceCretaor单例类
...
December 8, 2024
协程,构建,一个MVVM的frame
#
先是构建器
object ServiceCreator {
private const val BASE_URL="http://124.93.196.45:10001"
private val retrofot: Retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
fun <T> create(serviceClass:Class<T>): T= retrofot.create(serviceClass)
inline fun <reified T>create():T= create(T::class.java)
}
然后是接口
interface NewsService {
@GET("/prod-api/press/category/list")
fun getNewsType(): Call<NewsType>
@GET("/prod-api/press/press/list")
fun getNewsList(@Query("id") id:String,@Query("hot") hot:String):Call<NewsList>
}
写一个Network
object SmartCityNetwork {
private val userService= ServiceCreator.create<UserService>()
suspend fun login(data: LoginData) = userService.loginService(data).await()
private val AdvService=ServiceCreator.create<MetroService>()
suspend fun getAdv()=AdvService.getAdv().await()
private val newsService=ServiceCreator.create<NewsService>()
suspend fun getNewsType()=newsService.getNewsType().await()
suspend fun getNewsList(id :String,hot:String)= newsService.getNewsList(id,hot).await()
private suspend fun <T> Call<T>.await(): T {
return suspendCoroutine {
enqueue(object : Callback<T> {
override fun onResponse(call: Call<T>, response: Response<T>) {
val body = response.body()
if (body != null) it.resume(body)
else it.resumeWithException(RuntimeException("response body is null"))
}
override fun onFailure(call: Call<T>, t: Throwable) {
it.resumeWithException(t)
}
}) }
}
}
处理返回数据,其实可以分开写不同的接口
...
December 8, 2024
安卓 实现卡片
圆角程度 cardCornerRadius
阴影: cardElevation
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:cardCornerRadius="20dp"
android:layout_marginBottom="20dp"
android:layout_height="wrap_content"
android:layout_width="match_parent">
</androidx.cardview.widget.CardView>
December 8, 2024
异步get请求
private fun asyncGet() {
val url = ""
//创建request请求对象
val request = Request.Builder()
.url(url)
//.method()方法与.get()方法选取1种即可
.method("GET", null)
.build()
//创建call并调用enqueue()方法实现网络请求
OkHttpClient().newCall(request)
.enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
}
override fun onResponse(call: Call, response: Response) {
}
})
}
异步post请求
private fun asyncPost() {
val url = ""
//添加post请求参数
val requestBody = FormBody.Builder()
.add("userName", "name")
.add("passWord", "pass")
.build()
//发送josn
//var jsonObject = JSONObject()
//jsonObject.put("studentNumber","1713630001")
//jsonObject.put("password","123456")
//val requestBody=jsonObject.toString().toRequestBody("application/json".toMediaType())
//创建request请求对象
val request = Request.Builder()
.url(url)
.post(requestBody)
.build()
//创建call并调用enqueue()方法实现网络请求
OkHttpClient().newCall(request)
.enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
}
override fun onResponse(call: Call, response: Response) {
}
})
}
post json
...
December 8, 2024
由于是安卓原生开发,肯定是少不了写适配器的
考虑到即使是cv也很费时间,所以写一个通用的适配器
class GenericAdapter<T>(
private val items: List<T>,
private val layoutId: Int,
private val bind: (View, T) -> Unit
) : RecyclerView.Adapter<GenericAdapter.GenericViewHolder>() {
class GenericViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GenericViewHolder {
val view = LayoutInflater.from(parent.context).inflate(layoutId, parent, false)
return GenericViewHolder(view)
}
override fun onBindViewHolder(holder: GenericViewHolder, position: Int) {
bind(holder.itemView, items[position])
}
override fun getItemCount() = items.size
}
use
需要设置每行的个数 binding.welfareType.layoutManager = GridLayoutManager(requireContext(),2)
val adapter = GenericAdapter(listOf("Item 1", "Item 2"), R.layout.item_layout) { view, item ->
val textView: TextView = view.findViewById(R.id.textView)
textView.text = item
}
recyclerView.adapter = adapter
December 8, 2024
依旧是网络请求大头,以最快的方式写
协程 Retrofit
#
class Auth(private val token: String) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
.newBuilder()
.addHeader("Authorization","Bearer $token")
.build()
return chain.proceed( request)
}
}
fun createRetrofitWithAuth() :Retrofit{
return Retrofit.Builder()
.baseUrl("http://124.93.196.45:10193/")
.addConverterFactory(GsonConverterFactory.create())
.build()
}
fun createRetrofitWithAuth(token:String) :Retrofit{
val client=OkHttpClient.Builder()
.addInterceptor(Auth(token))
.build()
return Retrofit.Builder()
.baseUrl("http://124.93.196.45:10193/")
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build()
}
定义接口
interface ApiService {
@GET("/prod-api/api/public-welfare/public-welfare-type/list")
suspend fun WelfareType(): WelfareType
@POST("/prod-api/api/login")
suspend fun Login(@Body body:LoginBody): Login
@GET("/prod-api/api/public-welfare/ad-banner/list")
suspend fun Banner(): Banner
}
viewmodel
...
December 8, 2024
签名
#
生成jks文件
keytool -genkeypair -alias ljyhljyh -keypass ljyhljyh -keyalg RSA -keysize 1024 -validity 365 -keystore filename.jks -storepass ljyhljyh
重新签名
jarsigner -verbose -keystore filename.jks -storepass 501937 -signedjar ai3.apk -digestalg SHA1 -sigalg MD5withRSA ai2.apk filename
反编译
apktool d apk_name
重新打包
apktool b 文件 -o apk_name
September 3, 2023
起因
#
放假在家闲的皮爆,有空学习写了个(其实在很久之前就有打算了)
因为网易云音乐下载有很多但是加密的,虽然可以解密,但是也不方便管理,所以就有了这个软件,可以嵌入元数据(mp3和flac),包括歌词封面等。同步网易云歌单,可以删除本地文件
开发
#
至于学习flutter,最开始知道flutter是因为逆向系列了解到的,听说写ui很方便,之前也有用过java开发。java的语法还也不是很喜欢。
网易云的api 参考: NeteaseCloudMusicApi
学习编程语言我不是很喜欢看视频来,慢得很,需要什么直接去搜代码就行了。
先看看官方的文档,demo,自己需要什么功能直接百度
虽然可能了解不深刻,但是跟着视频写代码更烦人
使用
#
设置里面要先设置cookie,只需要cookie中的MUSIC_U的部分,以及保存路径,这个保存路径是歌单的保存路径,如果下载的话会根据歌单名为文件夹保存在下级。
元数据默认嵌入所有
先根据歌单id获取歌曲,若要下载需要收藏歌单,下载完成会检测本地歌单中多余歌曲并徐闻是否删除。
似乎就这点鸟功能