Android app making(is it possible to exploit github page?) - android

I am making a simple android application. To describe it, It shows a list of CPU models, like i7, i5, and AMD processor, so on.
I am not familiar with using a database! So I want to write txt file and store it on my GitHub homepage. Then, is it possible that load the txt file and show it in an android application?

Yes, you can.
You have to create a public repository and inside this repository store your text file as JSON File. see this
Note: repository must be public
and then you can access this as API URL using any httpclient like below.
val client = OkHttpClient() // have to add OkHttpClient in gradle file.
val request = Request.Builder()
.url("https://gitlab.com/jakir123/personaldictionary/-/raw/master/app_info.json") // the file link don't forget to replace blob with raw.
.build()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
EasyLog.logE(
"Exception in getting app info: ${e.localizedMessage}",
"AppInfoRepository"
)
}
override fun onResponse(call: Call, response: Response) {
val body = response.body()?.string()
val gson = GsonBuilder().create()
val appInfo = gson.fromJson(body, AppInfo::class.java)
// here you can access value like below
val versionCode = appInfo.version_code
val versionName = appInfo.version_name
}
})
AppInfo model class
data class AppInfo(
val version_code: Int,
val version_name: String,
val developer_name: String,
val developer_email: String,
val developer_image: String,
val playstore_link: String
)
Update: Java Example Code
OkHttpClient client = new OkHttpClient();
// GET request
Request request = new Request.Builder()
.url("your_url")
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Request request, IOException e) {
// handle on failure here.
}
#Override
public void onResponse(Call call, final Response response) throws IOException {
if (!response.isSuccessful()) {
throw new IOException("Unexpected code " + response);
} else {
// do something wih the response
String result = response.body().toString() // you will get your json text here as string.
Gson gson = new Gson(); // this library should be added in gradle
AppInfo appInfo= gson.fromJson(result, AppInfo.class);
// here you can access value like below
int versionCode = appInfo.version_code
String versionName = appInfo.version_name
}
}
});
AppInfo model class
public class AppInfo{
private int version_code;
private String version_name;
private String developer_name;
private String developer_email;
private String developer_image;
private String playstore_link;
//getters and setters
}

Related

Using Okhttp AWS Signer for s3 upload (PUT)

Can I use this signer https://github.com/babbel/okhttp-aws-signer for upload file to s3?
I use this https://bytes.babbel.com/en/articles/2019-01-03-okhttp-aws-signer.html reference when build my sample app:
class AwsSigingInterceptor(private val signer: OkHttpAwsV4Signer) : Interceptor {
private val dateFormat: ThreadLocal<SimpleDateFormat>
init {
dateFormat = object : ThreadLocal<SimpleDateFormat>() {
override fun initialValue(): SimpleDateFormat {
val localFormat = SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'", Locale.US)
localFormat.timeZone = TimeZone.getTimeZone("UTC")
return localFormat
}
}
}
override fun intercept(chain: Chain): Response =
chain.run {
val request = request()
val newRequest = request.newBuilder()
.addHeader("x-amz-date", dateFormat.get().format(clock.now()))
.addHeader("host", request.url().host())
.build()
val signed = signer.sign(newRequest, "<accessKeyId>", "<secretAccessKey>")
proceed(signed)
}
}
but I didn't see any x-amz-content-sha256 there.. only additional Authorization header added. When I try the debug result header in Advanced Rest Client or Postman, it says "Missing required header for this request: x-amz-content-sha256"

How to develop an android application separating data and view layer

I am new to Android development. have an android application in koltin wherein I have to make an http post request to get a list of data as response.
I have done that in activity class as follows.
MainActivity.kt
class MainActivity : AppCompatActivity(), {
private fun getAppList() {
var builder = AlertDialog.Builder(this#MainActivity)
builder.setTitle("App Response")
doAsync {
sslCertficate.disableSSLCertificateChecking()
var headers = HashMap<String, String>()
headers["Content-type"] = "application/json; charset=UTF-8"
val res = HTTPClient("https://sample-myapi-launcher.prod.com/list")
.setMethod("POST")
.setHeaders(headers)
.setBody(getRequestBody(userInfo.toString()))
.getResponse()
.response
uiThread {
builder.setMessage(res)
var dialog: AlertDialog = builder.create()
dialog.show()
}
Log.e("Response List", res)
}
}
private fun getRequestBody(userInfo: String): String {
//code for geting request body
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_navigator)
setSupportActionBar(toolbar)
//calling api request method
getAppList()
}
}
I could achieve my result through this, But I don't want to put all the work in the activity thread. Can someone guide on the correct approach to achieve this?
Or help me with some documentation.
This is the Android lifecycle-aware components codelab. It will do exatelly what you ask for. Here is the Architecture components part of the Android Jetpack and it is a set of Android libraries that help you structure your app in a way that is robust, testable, and maintainable.
Here is also the android-sunflower A gardening app illustrating Android development best practices with Android Jetpack.
to do networking I suggest you to use Retrofit2: Retrofit
Anyway to do networking operation in another thread you need to start a new AsyncTask from you activity and do the networking operations inside it.
In retrofit all this is much more simple!
(Sorry but I don't have Kotlin example for that below!)
Java Example without Retrofit:
( This was a my old project, so it isn't so good ^^)
/* Really Simple Class I made to do networking operations (so use Retrofit or make a better one (: (I suggest you, again, to use Retrofit!) */
public class DBConnection {
public String performPostCall(String requestURL, HashMap<String, String> postDataParams )
{
URL url;
String response = "";
try {
url = new URL(requestURL);
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setReadTimeout(15000);
conn.setConnectTimeout(15000);
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setHostnameVerifier(new HostnameVerifier() {
#Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
conn.setSSLSocketFactory((SSLSocketFactory) SSLSocketFactory.getDefault());
conn.connect();
DataOutputStream dStream = new DataOutputStream(conn.getOutputStream());
dStream.writeBytes(getPostDataString(postDataParams));
dStream.flush();
dStream.close();
int responseCode = conn.getResponseCode();
if (responseCode == HttpsURLConnection.HTTP_OK) {
String line;
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
while ((line=br.readLine()) != null) {
response += line;
}
} else {
response = "";
}
conn.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
return response;
}
private String getPostDataString(HashMap<String, String> params) throws UnsupportedEncodingException
{
StringBuilder result = new StringBuilder();
boolean first = true;
for(Map.Entry<String, String> entry : params.entrySet()){
if (first) {
first = false;
} else {
result.append("&");
}
result.append(entry.getKey());
result.append("=");
result.append(entry.getValue());
}
return result.toString();
}
}
// AsyncTask to do Async Networking operations:
public class YourTask extends AsyncTask<String, Void, String> {
private String yourData...;
public YourTask(String token){
// Set Your Data
}
// "String..." is an array of arguments so you get the arguments usign: params[i]
#Override
protected String doInBackground(String... params)
{
DBConnection dbConn = new DBConnection();
String stampAnswer;
try{
Integer param1 = Integer.parseInt(params[0]);
Integer param2 = Integer.parseInt(params[1]);
answer = dbConn.netwokirngOperation([..]);
}catch(NumberFormatException nfe){
nfe.getStackTrace();
stampAnswer = "";
Log.e("YourTask", " NumberFormatException:");
}
return answer;
}
protected void onPostExecute(String result) {
Log.e("YourTask => ", " Result:" + result);
}
}
// To call the task do in your activity and do async networking operation without wait for result (this mean you need to save the data inside Realm DB [REALM][2] or using SQLite DB and then get them when the networking operations ended (you can use an Observable and set it when the networking operation end, send a broadcast message and set a receiver in you activity, or any other method):
new YourTask(<put_here_your_asynctask_constructor_args>).execute(params);
// To call the task and do async networking operation but wait to get the result returned by the "doInBackground" method of the AsyncTask:
new YourTask(<put_here_your_asynctask_constructor_args>).execute(params).get();
But is better if you use interface and callback to return the result from the AsyncTask when it ended, example:
/** in You Activity. Because in the Interface you use generic types (the 'T') you can specific the type of object returned by the interface inside the '<T>' if the interface WILL ALWAYS RETURN THE SAME OBJECT TYPE!
If it WILL RETURN DIFFERENT OBJECT TYPES you MUST don't specific the type inside the '<T>', but you have to cast the return inside a switch statement to know which object is returned (to do that you can add a 'int requestCase' to the interface so you know which case returned!) **/
public class YourActivity extends AppCompatActivity
implements IYourCallback<YourObjectTypesReturned>{
public interface IYourCallback<T>{
onNetOperationSuccess(List<T> answer)
onNetOperationError(Throwable t)
}
/** IMPLEMENTS HERE YOUR ACTIVITY BODY WITH THE INTERFACE METHODS ! **/
// Then call your AsyncTask where you want and pass it your context which implements the interface ( because you are in activity your context with the interface is "this"!
new YourTask(this).execute(params);
// Then inside your AsyncTask:
public class YourTask extends AsyncTask<String, Void, String> {
private IYourCallback mCallback;
public YourTask(Context context){
try{
mCallback = (IYourCallback) mCallback;
} catch(ClassCastException e){
onException(e); // Manage the exception and stop the operation
}
}
/** THEN IMPLEMENT YOU IN BACKGROUND... AND WHEN THE NETWORKING OPERATION IS FINISHED USE THE CALLBACK TO RETURN BACK YOUR RESULT, SO THE METHOD IN YOUR ACTIVITY WILL GET TRIGGERED AND YOU CAN CONTINUE TO DO YOUR OPERATIONS! So do: **/
if(success)
mCallback.onNetOperationSuccess(myListAnswer)
else
mCallback.onNetOperationError(error) // Throwable or exception
( Kotlin for the implementation of Retrofit! I started use Kotlin 5 days ago, so I don't know if this is the best use (: )
Example with Retrofit:
/* This is a RetrofitHelper which init the Retrofit instance and where you should put your networking methods. Then to do a networking operation you have to get this instance using (RetrofitHelper.getInstance().yourNetworkingOperation(...) ).
Anyway here there isn't the asynchronous part, you can get it in the link of my other comment below!
I don't have complete this class yet! */
class RetrofitHelper(baseUrl: String){
private val TAG = this.javaClass.name
// Timeouts
private val CONNECT_TIMEOUT = "CONNECT_TIMEOUT"
private val READ_TIMEOUT = "READ_TIMEOUT"
private val WRITE_TIMEOUT = "WRITE_TIMEOUT"
// Header Names
private val BASE_REQ_HEADER_NAME = "BLEDataBinder"
private val REQ_HEADER_NAME = "$BASE_REQ_HEADER_NAME.Request"
private val REQ_HEADER_VERSION_NAME = "$BASE_REQ_HEADER_NAME.VersionName"
private val REQ_HEADER_VERSION_CODE = "$BASE_REQ_HEADER_NAME.VersionCode"
private val REQ_HEADER_DEVICE_IMEI = "$BASE_REQ_HEADER_NAME.DeviceIMEI"
private val REQ_HEADER_DEVICE_UNIQUE_ID = "$BASE_REQ_HEADER_NAME.DeviceUniqueID"
private val REQ_HEADER_DEVICE_MODEL = "$BASE_REQ_HEADER_NAME.DeviceModel"
private val REQ_HEADER_ANDROID_RELEASE = "$BASE_REQ_HEADER_NAME.AndroidRelease"
// Header Values
private val REQ_HEADER_VALUE = "emax"
// Labels
private val LABEL_INIT = "Init RetrofitHelper"
private var mBaseUrl: String
private var mGson: Gson
private var mRetrofit: Retrofit
companion object {
#Volatile private var mInstance: RetrofitHelper? = null
fun getInstance() = mInstance
fun initInstance(baseUrl: String): RetrofitHelper =
mInstance ?: synchronized(this){
mInstance ?: newInstance(baseUrl).also { mInstance = it }
}
private fun newInstance(baseUrl: String) = RetrofitHelper(baseUrl)
}
init {
LogUtils.iLog(TAG, "START $LABEL_INIT")
val httpClient = OkHttpClient.Builder()
httpClient.addInterceptor( getInterceptor() )
httpClient.addInterceptor( getLoggingInterceptor() )
this.mBaseUrl = baseUrl
mGson = getGson()
mRetrofit = getRetrofit(httpClient.build())
LogUtils.iLog(TAG, "END $LABEL_INIT")
}
/* START Private Methods */
private fun getRetrofit(httpClient: OkHttpClient): Retrofit{
return Retrofit.Builder()
.baseUrl(mBaseUrl)
.addConverterFactory(GsonConverterFactory.create(mGson))
.client(httpClient)
.build()
}
private fun getGson(): Gson{
return GsonBuilder()
.setDateFormat(Constants.DATETIME_FORMAT_DB)
.registerTypeAdapter(Boolean::class.javaObjectType, BooleanDeserializer())
.create()
}
private fun getLoggingInterceptor() =
HttpLoggingInterceptor {
getLoggingInterceptorLogger()
}.also { it.level = HttpLoggingInterceptor.Level.BODY }
private fun getLoggingInterceptorLogger() =
HttpLoggingInterceptor.Logger {
message -> HyperLog.v(TAG, message)
}
private fun getInterceptor(): Interceptor =
Interceptor {
buildInterceptorResponse(it)
}
private fun buildInterceptorResponse(chain: Interceptor.Chain): Response {
val builder: Request.Builder = chain.request().newBuilder().addHeader(REQ_HEADER_NAME, REQ_HEADER_VALUE)
setRequestHeaderVersionName(builder)
setRequestHeaderVersionCode(builder)
setRequestHeaderDeviceIMEI(builder)
setRequestHeaderDeviceUniqueID(builder)
setRequestHeaderDeviceModel(builder)
setRequestHeaderAndroidRelease(builder)
/* This part let you set custom timeout for different api call inside the "RetrofitAPI" interface using that labels: (example inside the RetrofitAPI interface)
public interface RetrofitAPI {
#Headers({RetrofitHelper.CONNECT_TIMEOUT + ":100000", RetrofitHelper.READ_TIMEOUT + ":100000"})
#FormUrlEncoded
#POST
Call<JsonObject> doBaseJsonRequest(#Url String url, #Field("params") String params);
}
*/
var connectTimeout = chain.connectTimeoutMillis()
var readTimeout = chain.readTimeoutMillis()
var writeTimeout = chain.writeTimeoutMillis()
val request = chain.request()
if(!TextUtils.isEmpty(request.header(CONNECT_TIMEOUT))){
connectTimeout = request.header(CONNECT_TIMEOUT)!!.toInt()
}
if(!TextUtils.isEmpty(request.header(READ_TIMEOUT))){
readTimeout = request.header(READ_TIMEOUT)!!.toInt()
}
if(!TextUtils.isEmpty(request.header(WRITE_TIMEOUT))){
writeTimeout = request.header(WRITE_TIMEOUT)!!.toInt()
}
builder.removeHeader(CONNECT_TIMEOUT)
builder.removeHeader(READ_TIMEOUT)
builder.removeHeader(WRITE_TIMEOUT)
return chain
.withConnectTimeout(connectTimeout, TimeUnit.MILLISECONDS)
.withReadTimeout(readTimeout, TimeUnit.MILLISECONDS)
.withWriteTimeout(writeTimeout, TimeUnit.MILLISECONDS)
.proceed(builder.build())
}
/*private fun setRequestHeaders(builder: Request.Builder): Request.Builder{
if(!TextUtils.isEmpty(AppEnvironment.getInstance()!!.mVersionName)){
builder.addHeader(REQ_HEADER_VERSION_NAME, AppEnvironment.getInstance()!!.mVersionName!!)
}
if(!TextUtils.isEmpty(AppEnvironment.getInstance()!!.mVersionCode.toString())){
builder.addHeader(REQ_HEADER_VERSION_CODE, AppEnvironment.getInstance()!!.mVersionName!!)
}
if(!TextUtils.isEmpty(AppEnvironment.getInstance()!!.mDeviceIMEI)){
builder.addHeader(REQ_HEADER_DEVICE_IMEI, AppEnvironment.getInstance()!!.mVersionName!!)
}
if(!TextUtils.isEmpty(AppEnvironment.getInstance()!!.mDeviceUniqueID)){
builder.addHeader(REQ_HEADER_DEVICE_UNIQUE_ID, AppEnvironment.getInstance()!!.mVersionName!!)
}
if(!TextUtils.isEmpty(AppEnvironment.getInstance()!!.mDeviceModel)){
builder.addHeader(REQ_HEADER_DEVICE_MODEL, AppEnvironment.getInstance()!!.mVersionName!!)
}
if(!TextUtils.isEmpty(AppEnvironment.getInstance()!!.mAndroidRelease)){
builder.addHeader(REQ_HEADER_ANDROID_RELEASE, AppEnvironment.getInstance()!!.mVersionName!!)
}
return builder
}*/
private fun setRequestHeaderVersionName(builder: Request.Builder){
if(!TextUtils.isEmpty(AppEnvironment.getInstance()!!.mVersionName)){
builder.addHeader(REQ_HEADER_VERSION_NAME, AppEnvironment.getInstance()!!.mVersionName!!)
}
}
private fun setRequestHeaderVersionCode(builder: Request.Builder){
if(!TextUtils.isEmpty(AppEnvironment.getInstance()!!.mVersionCode.toString())){
builder.addHeader(REQ_HEADER_VERSION_CODE, AppEnvironment.getInstance()!!.mVersionName!!)
}
}
private fun setRequestHeaderDeviceIMEI(builder: Request.Builder){
if(!TextUtils.isEmpty(AppEnvironment.getInstance()!!.mDeviceIMEI)){
builder.addHeader(REQ_HEADER_DEVICE_IMEI, AppEnvironment.getInstance()!!.mVersionName!!)
}
}
private fun setRequestHeaderDeviceUniqueID(builder: Request.Builder){
if(!TextUtils.isEmpty(AppEnvironment.getInstance()!!.mDeviceUniqueID)){
builder.addHeader(REQ_HEADER_DEVICE_UNIQUE_ID, AppEnvironment.getInstance()!!.mVersionName!!)
}
}
private fun setRequestHeaderDeviceModel(builder: Request.Builder){
if(!TextUtils.isEmpty(AppEnvironment.getInstance()!!.mDeviceModel)){
builder.addHeader(REQ_HEADER_DEVICE_MODEL, AppEnvironment.getInstance()!!.mVersionName!!)
}
}
private fun setRequestHeaderAndroidRelease(builder: Request.Builder){
if(!TextUtils.isEmpty(AppEnvironment.getInstance()!!.mAndroidRelease)){
builder.addHeader(REQ_HEADER_ANDROID_RELEASE, AppEnvironment.getInstance()!!.mVersionName!!)
}
}
/* END Private Methods */
}
LINK TO MY COMMENT FOR ASYNC RETROFIT USAGE WITH GENERIC TYPES METHODS AND CALLBACK INTERFACES:
RETROFIT ASYNC WITH GENERIC TYPES
(This is in java but you can easily translate it in Kotlin! Also I suggest you to learn Java too because Kotlin is a scripting language built up Java so it translates the code in Java operations whose sometimes are really bigger (and sometimes slower) than if you wrote the code in Java! So, now I am learning Kotlin after learned Java for android applications, Kotlin is a really good, smart, beautiful and fast programming language for apps and I will use it to do fast and smart scripts inside my applications or for simple applications, but I will use Java too because with it you can generate faster code.
Hope this is helpful,
Bye and Good Coding! (:

How to get Json Response in Arraylist from List<Item> using RxJava and Kotlin

Facing Problem on getting Response in ArrayList.
I have following Respose on String value
var res_message: String = ""
res_message = "${result.vehicletypes} "
Getting below Value on this String
[VehicleType(_id=1, vehicleType=Hatchback, __v=0),
VehicleType(_id=2, vehicleType=Maruti, __v=0),
VehicleType(_id=3, vehicleType=Honda, __v=0),
VehicleType(_id=4, vehicleType=Bike, __v=0)]
Retrofit Result is
vehicletypes = {ArrayList#6055} size = 4
0 = {Model$VehicleType#6058} "VehicleType(_id=1,
vehicleType=Hatchback, __v=0)"
1 = {Model$VehicleType#6059} "VehicleType(_id=2,
vehicleType=Maruti, __v=0)"
2 = {Model$VehicleType#6060} "VehicleType(_id=3,
vehicleType=Honda, __v=0)"
3 = {Model$VehicleType#6061} "VehicleType(_id=4,
vehicleType=Bike, __v=0)"
Below Code snippest sending request to API.
disposable = apiServices.getVehicle(token)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ result ->
res_message = "${result.vehicletypes} "
Log.d("Type==", res_message)
},
{ error ->
res_message = "${error.message}"
// validateToken()
}
)
Model Class
data class Vehicles(val success: Boolean, val vehicletypes: List<VehicleType>, val message: String)
data class VehicleType(val _id: String, val vehicleType: String, val __v: String)
I want to get this value on Arralist VehicleType List on below vehicleListArray
private var vehicleListArray: ArrayList<Model.VehicleType>? = null
How we can achieve this.
Thanks in advance.
Assuming that what you are trying to parse is a response from a service that is able to send you propper format for lists (eg Json) than
Retrofit can handle parsing lists with ease.
In your apiService definition:
fun getPeople(token: Whatever): Observable<List<VehicleType>>
And if you don't have it already:
Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create(gson))
I got solution I have to handle Respose as below code snippet.
private fun getVehicleType() {
disposable?.add(apiServices.getVehicle(token)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(this::handleResponse, this::handleError))
}
private fun handleResponse(vehicles: Model.Vehicles) {
VehiclesArrayList = ArrayList(vehicles.vehicletypes)
Log.d("type==","n--"+VehiclesArrayList )
mAdapter = DataAdapter(VehiclesArrayList !!, this)
v_android_list.adapter = mAdapter
}
private fun handleError(error: Throwable) {
Log.d("type", error.localizedMessage)
Toast.makeText(context, "Error ${error.localizedMessage}", Toast.LENGTH_SHORT).show()
}

HTTP Request in Android with Kotlin

I want to do a login validation using POST method and to get some information using GET method.
I've URL, server Username and Password already of my previous project.
For Android, Volley is a good place to get started. For all platforms, you might also want to check out ktor client or http4k which are both good libraries.
However, you can also use standard Java libraries like java.net.HttpURLConnection
which is part of the Java SDK:
fun sendGet() {
val url = URL("http://www.google.com/")
with(url.openConnection() as HttpURLConnection) {
requestMethod = "GET" // optional default is GET
println("\nSent 'GET' request to URL : $url; Response Code : $responseCode")
inputStream.bufferedReader().use {
it.lines().forEach { line ->
println(line)
}
}
}
}
Or simpler:
URL("https://google.com").readText()
Send HTTP POST/GET request with parameters using HttpURLConnection :
POST with Parameters:
fun sendPostRequest(userName:String, password:String) {
var reqParam = URLEncoder.encode("username", "UTF-8") + "=" + URLEncoder.encode(userName, "UTF-8")
reqParam += "&" + URLEncoder.encode("password", "UTF-8") + "=" + URLEncoder.encode(password, "UTF-8")
val mURL = URL("<Your API Link>")
with(mURL.openConnection() as HttpURLConnection) {
// optional default is GET
requestMethod = "POST"
val wr = OutputStreamWriter(getOutputStream());
wr.write(reqParam);
wr.flush();
println("URL : $url")
println("Response Code : $responseCode")
BufferedReader(InputStreamReader(inputStream)).use {
val response = StringBuffer()
var inputLine = it.readLine()
while (inputLine != null) {
response.append(inputLine)
inputLine = it.readLine()
}
println("Response : $response")
}
}
}
GET with Parameters:
fun sendGetRequest(userName:String, password:String) {
var reqParam = URLEncoder.encode("username", "UTF-8") + "=" + URLEncoder.encode(userName, "UTF-8")
reqParam += "&" + URLEncoder.encode("password", "UTF-8") + "=" + URLEncoder.encode(password, "UTF-8")
val mURL = URL("<Yout API Link>?"+reqParam)
with(mURL.openConnection() as HttpURLConnection) {
// optional default is GET
requestMethod = "GET"
println("URL : $url")
println("Response Code : $responseCode")
BufferedReader(InputStreamReader(inputStream)).use {
val response = StringBuffer()
var inputLine = it.readLine()
while (inputLine != null) {
response.append(inputLine)
inputLine = it.readLine()
}
it.close()
println("Response : $response")
}
}
}
Using only the standard library with minimal code!
thread {
val json = try {
URL(url).readText()
} catch (e: Exception) {
return#thread
}
runOnUiThread { displayOrWhatever(json) }
}
This starts a GET request on a new thread, leaving the UI thread to respond to user input. However, we can only modify UI elements from the main/UI thread, so we actually need a runOnUiThread block to show the result to our user. This enqueues our display code to be run on the UI thread soon.
The try/catch is there so your app won't crash if you make a request with your phone's internet off. Add your own error handling (e.g. showing a Toast) as you please.
.readText() is not part of the java.net.URL class but a Kotlin extension method, Kotlin "glues" this method onto URL. This is enough for plain GET requests, but for more control and POST requests you need something like the Fuel library.
Have a look at Fuel library, a sample GET request
"https://httpbin.org/get"
.httpGet()
.responseString { request, response, result ->
when (result) {
is Result.Failure -> {
val ex = result.getException()
}
is Result.Success -> {
val data = result.get()
}
}
}
// You can also use Fuel.get("https://httpbin.org/get").responseString { ... }
// You can also use FuelManager.instance.get("...").responseString { ... }
A sample POST request
Fuel.post("https://httpbin.org/post")
.jsonBody("{ \"foo\" : \"bar\" }")
.also { println(it) }
.response { result -> }
Their documentation can be found here
​
I think using okhttp is the easiest solution. Here you can see an example for POST method, sending a json, and with auth.
val url = "https://example.com/endpoint"
val client = OkHttpClient()
val JSON = MediaType.get("application/json; charset=utf-8")
val body = RequestBody.create(JSON, "{\"data\":\"$data\"}")
val request = Request.Builder()
.addHeader("Authorization", "Bearer $token")
.url(url)
.post(body)
.build()
val response = client . newCall (request).execute()
println(response.request())
println(response.body()!!.string())
Remember to add this dependency to your project https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp
UPDATE: July 7th, 2019
I'm gonna give two examples using latest Kotlin (1.3.41), OkHttp (4.0.0) and Jackson (2.9.9).
UPDATE: January 25th, 2021
Everything is okay with the most updated versions.
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.module/jackson-module-kotlin -->
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-kotlin</artifactId>
<version>2.12.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.0</version>
</dependency>
Get Method
fun get() {
val client = OkHttpClient()
val url = URL("https://reqres.in/api/users?page=2")
val request = Request.Builder()
.url(url)
.get()
.build()
val response = client.newCall(request).execute()
val responseBody = response.body!!.string()
//Response
println("Response Body: " + responseBody)
//we could use jackson if we got a JSON
val mapperAll = ObjectMapper()
val objData = mapperAll.readTree(responseBody)
objData.get("data").forEachIndexed { index, jsonNode ->
println("$index $jsonNode")
}
}
POST Method
fun post() {
val client = OkHttpClient()
val url = URL("https://reqres.in/api/users")
//just a string
var jsonString = "{\"name\": \"Rolando\", \"job\": \"Fakeador\"}"
//or using jackson
val mapperAll = ObjectMapper()
val jacksonObj = mapperAll.createObjectNode()
jacksonObj.put("name", "Rolando")
jacksonObj.put("job", "Fakeador")
val jacksonString = jacksonObj.toString()
val mediaType = "application/json; charset=utf-8".toMediaType()
val body = jacksonString.toRequestBody(mediaType)
val request = Request.Builder()
.url(url)
.post(body)
.build()
val response = client.newCall(request).execute()
val responseBody = response.body!!.string()
//Response
println("Response Body: " + responseBody)
//we could use jackson if we got a JSON
val objData = mapperAll.readTree(responseBody)
println("My name is " + objData.get("name").textValue() + ", and I'm a " + objData.get("job").textValue() + ".")
}
Maybe the simplest GET
For everybody stuck with NetworkOnMainThreadException for the other solutions: use AsyncTask or, even shorter, (yet still experimental) Coroutines:
launch {
val jsonStr = URL("url").readText()
}
If you need to test with plain http don't forget to add to your manifest:
android:usesCleartextTraffic="true"
For the experimental Coroutines you have to add to build.gradle as of 10/10/2018:
kotlin {
experimental {
coroutines 'enable'
}
}
dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:0.24.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:0.24.0"
...
If you are using Kotlin, you might as well keep your code as succinct as possible. The run method turns the receiver into this and returns the value of the block.
this as HttpURLConnection creates a smart cast. bufferedReader().readText() avoids a bunch of boilerplate code.
return URL(url).run {
openConnection().run {
this as HttpURLConnection
inputStream.bufferedReader().readText()
}
}
You can also wrap this into an extension function.
fun URL.getText(): String {
return openConnection().run {
this as HttpURLConnection
inputStream.bufferedReader().readText()
}
}
And call it like this
return URL(url).getText()
Finally, if you are super lazy, you can extend the String class instead.
fun String.getUrlText(): String {
return URL(this).run {
openConnection().run {
this as HttpURLConnection
inputStream.bufferedReader().readText()
}
}
}
And call it like this
return "http://somewhere.com".getUrlText()
You can use kohttp library. It is a Kotlin DSL HTTP client. It supports the features of square.okhttp and provides a clear DSL for them. KoHttp async calls are powered by coroutines.
httpGet extension function
val response: Response = "https://google.com/search?q=iphone".httpGet()
you can also use async call with coroutines
val response: Deferred<Response> = "https://google.com/search?q=iphone".asyncHttpGet()
or DSL function for more complex requests
val response: Response = httpGet {
host = "google.com"
path = "/search"
param {
"q" to "iphone"
"safe" to "off"
}
}
You can find more details in docs
To get it with gradle use
implementation 'io.github.rybalkinsd:kohttp:0.12.0'
Without adding additional dependencies, this works. You don't need Volley for this. This works using the current version of Kotlin as of Dec 2018: Kotlin 1.3.10
If using Android Studio, you'll need to add this declaration in your AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
You should manually declare imports here. The auto-import tool caused me many conflicts.:
import android.os.AsyncTask
import java.io.BufferedReader
import java.io.InputStreamReader
import java.io.OutputStream
import java.io.OutputStreamWriter
import java.net.URL
import java.net.URLEncoder
import javax.net.ssl.HttpsURLConnection
You can't perform network requests on a background thread. You must subclass AsyncTask.
To call the method:
NetworkTask().execute(requestURL, queryString)
Declaration:
private class NetworkTask : AsyncTask<String, Int, Long>() {
override fun doInBackground(vararg parts: String): Long? {
val requestURL = parts.first()
val queryString = parts.last()
// Set up request
val connection: HttpsURLConnection = URL(requestURL).openConnection() as HttpsURLConnection
// Default is GET so you must override this for post
connection.requestMethod = "POST"
// To send a post body, output must be true
connection.doOutput = true
// Create the stream
val outputStream: OutputStream = connection.outputStream
// Create a writer container to pass the output over the stream
val outputWriter = OutputStreamWriter(outputStream)
// Add the string to the writer container
outputWriter.write(queryString)
// Send the data
outputWriter.flush()
// Create an input stream to read the response
val inputStream = BufferedReader(InputStreamReader(connection.inputStream)).use {
// Container for input stream data
val response = StringBuffer()
var inputLine = it.readLine()
// Add each line to the response container
while (inputLine != null) {
response.append(inputLine)
inputLine = it.readLine()
}
it.close()
// TODO: Add main thread callback to parse response
println(">>>> Response: $response")
}
connection.disconnect()
return 0
}
protected fun onProgressUpdate(vararg progress: Int) {
}
override fun onPostExecute(result: Long?) {
}
}
GET and POST using OkHttp
private const val CONNECT_TIMEOUT = 15L
private const val READ_TIMEOUT = 15L
private const val WRITE_TIMEOUT = 15L
private fun performPostOperation(urlString: String, jsonString: String, token: String): String? {
return try {
val client = OkHttpClient.Builder()
.connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
.build()
val body = jsonString.toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull())
val request = Request.Builder()
.url(URL(urlString))
.header("Authorization", token)
.post(body)
.build()
val response = client.newCall(request).execute()
response.body?.string()
}
catch (e: IOException) {
e.printStackTrace()
null
}
}
private fun performGetOperation(urlString: String, token: String): String? {
return try {
val client = OkHttpClient.Builder()
.connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
.build()
val request = Request.Builder()
.url(URL(urlString))
.header("Authorization", token)
.get()
.build()
val response = client.newCall(request).execute()
response.body?.string()
}
catch (e: IOException) {
e.printStackTrace()
null
}
}
Object serialization and deserialization
#Throws(JsonProcessingException::class)
fun objectToJson(obj: Any): String {
return ObjectMapper().writeValueAsString(obj)
}
#Throws(IOException::class)
fun jsonToAgentObject(json: String?): MyObject? {
return if (json == null) { null } else {
ObjectMapper().readValue<MyObject>(json, MyObject::class.java)
}
}
Dependencies
Put the following lines in your gradle (app) file. Jackson is optional. You can use it for object serialization and deserialization.
implementation 'com.squareup.okhttp3:okhttp:4.3.1'
implementation 'com.fasterxml.jackson.core:jackson-core:2.9.8'
implementation 'com.fasterxml.jackson.core:jackson-annotations:2.9.8'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.9.8'
You can use this library Fuel Library as well, which makes it further easier.
val map = mutableMapOf<String, String>()
map.put("id","629eeb9da9d8f50016e1af96")
val httpAsync = url
.httpPost()
.jsonBody(
Gson().toJson(map) // for json string
)
.responseString { request, response, result -> //do something with the response }

OKHTTP 3 Tracking Multipart upload progress

How can I track progress of upload in OkHttp 3
I can find answers for v2 but not v3, like this
A sample Multipart request from OkHttp recipes
private static final String IMGUR_CLIENT_ID = "...";
private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
// Use the imgur image upload API as documented at https://api.imgur.com/endpoints/image
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("title", "Square Logo")
.addFormDataPart("image", "logo-square.png",
RequestBody.create(MEDIA_TYPE_PNG, new File("website/static/logo-square.png")))
.build();
Request request = new Request.Builder()
.header("Authorization", "Client-ID " + IMGUR_CLIENT_ID)
.url("https://api.imgur.com/3/image")
.post(requestBody)
.build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
}
You can decorate your OkHttp request body to count the number of bytes written when writing it; in order to accomplish this task, wrap your MultiPart RequestBody in this RequestBody with an instance of Listener and Voila!
public class ProgressRequestBody extends RequestBody {
protected RequestBody mDelegate;
protected Listener mListener;
protected CountingSink mCountingSink;
public ProgressRequestBody(RequestBody delegate, Listener listener) {
mDelegate = delegate;
mListener = listener;
}
#Override
public MediaType contentType() {
return mDelegate.contentType();
}
#Override
public long contentLength() {
try {
return mDelegate.contentLength();
} catch (IOException e) {
e.printStackTrace();
}
return -1;
}
#Override
public void writeTo(BufferedSink sink) throws IOException {
mCountingSink = new CountingSink(sink);
BufferedSink bufferedSink = Okio.buffer(mCountingSink);
mDelegate.writeTo(bufferedSink);
bufferedSink.flush();
}
protected final class CountingSink extends ForwardingSink {
private long bytesWritten = 0;
public CountingSink(Sink delegate) {
super(delegate);
}
#Override
public void write(Buffer source, long byteCount) throws IOException {
super.write(source, byteCount);
bytesWritten += byteCount;
mListener.onProgress((int) (100F * bytesWritten / contentLength()));
}
}
public interface Listener {
void onProgress(int progress);
}
}
Check this link for more.
I was unable to get any of the answers to work for me. The issue was that the progress would run to 100% before the image was uploaded hinting that some buffer was getting filled prior to the data being sent over the wire. After some research, I found this was indeed the case and that buffer was the Socket send buffer. Providing a SocketFactory to the OkHttpClient finally worked. My Kotlin code is as follows...
First, As others, I have a CountingRequestBody which is used to wrap the MultipartBody.
class CountingRequestBody(var delegate: RequestBody, private var listener: (max: Long, value: Long) -> Unit): RequestBody() {
override fun contentType(): MediaType? {
return delegate.contentType()
}
override fun contentLength(): Long {
try {
return delegate.contentLength()
} catch (e: IOException) {
e.printStackTrace()
}
return -1
}
override fun writeTo(sink: BufferedSink) {
val countingSink = CountingSink(sink)
val bufferedSink = Okio.buffer(countingSink)
delegate.writeTo(bufferedSink)
bufferedSink.flush()
}
inner class CountingSink(delegate: Sink): ForwardingSink(delegate) {
private var bytesWritten: Long = 0
override fun write(source: Buffer, byteCount: Long) {
super.write(source, byteCount)
bytesWritten += byteCount
listener(contentLength(), bytesWritten)
}
}
}
I'm using this in Retrofit2. A general usage would be something like this:
val builder = MultipartBody.Builder()
// Add stuff to the MultipartBody via the Builder
val body = CountingRequestBody(builder.build()) { max, value ->
// Progress your progress, or send it somewhere else.
}
At this point, I was getting progress, but I would see 100% and then a long wait while the data was uploading. The key was that the socket was, by default in my setup, configured to buffer 3145728 bytes of send data. Well, my images were just under that and the progress was showing the progress of filling that socket send buffer. To mitigate that, create a SocketFactory for the OkHttpClient.
class ProgressFriendlySocketFactory(private val sendBufferSize: Int = DEFAULT_BUFFER_SIZE) : SocketFactory() {
override fun createSocket(): Socket {
return setSendBufferSize(Socket())
}
override fun createSocket(host: String, port: Int): Socket {
return setSendBufferSize(Socket(host, port))
}
override fun createSocket(host: String, port: Int, localHost: InetAddress, localPort: Int): Socket {
return setSendBufferSize(Socket(host, port, localHost, localPort))
}
override fun createSocket(host: InetAddress, port: Int): Socket {
return setSendBufferSize(Socket(host, port))
}
override fun createSocket(address: InetAddress, port: Int, localAddress: InetAddress, localPort: Int): Socket {
return setSendBufferSize(Socket(address, port, localAddress, localPort))
}
private fun setSendBufferSize(socket: Socket): Socket {
socket.sendBufferSize = sendBufferSize
return socket
}
companion object {
const val DEFAULT_BUFFER_SIZE = 2048
}
}
And during config, set it.
val clientBuilder = OkHttpClient.Builder()
.socketFactory(ProgressFriendlySocketFactory())
As others have mentioned, Logging the request body may effect this and cause the data to be read more than once. Either don't log the body, or what I do is turn it off for CountingRequestBody. To do so, I wrote my own HttpLoggingInterceptor and it solves this and other issues (like logging MultipartBody). But that's beyond the scope fo this question.
if(requestBody is CountingRequestBody) {
// don't log the body in production
}
The other issues was with MockWebServer. I have a flavor that uses MockWebServer and json files so my app can run without a network so I can test without that burden. For this code to work, the Dispatcher needs to read the body data. I created this Dispatcher to do just that. Then it forwards the dispatch to another Dispatcher, such as the default QueueDispatcher.
class BodyReadingDispatcher(val child: Dispatcher): Dispatcher() {
override fun dispatch(request: RecordedRequest?): MockResponse {
val body = request?.body
if(body != null) {
val sink = ByteArray(1024)
while(body.read(sink) >= 0) {
Thread.sleep(50) // change this time to work for you
}
}
val response = child.dispatch(request)
return response
}
}
You can use this in the MockWebServer as:
var server = MockWebServer()
server.setDispatcher(BodyReadingDispatcher(QueueDispatcher()))
This is all working code in my project. I did pull it out of illustration purposes. If it does not work for you out of the box, I apologize.
According to Sourabh's answer, I want tell that field of CountingSink
private long bytesWritten = 0;
must be moved into ProgressRequestBody class

Categories

Resources