Android Programjaim

Android, Flutter/Dart, Kotlin, Java, Unity, HarmonyOS

Butterknife, android annotation

2016. április 01. 12:15 - lacas8282

Jake Wharton ismet, es hogy mi is ez? DI ezis, dependency injection

  @Bind(R.id.title) TextView title;
  @Bind(R.id.subtitle) TextView subtitle;
  @Bind(R.id.footer) TextView footer;

azaz a view-eket tudjuk bind-olni, inject-elni es nem kell findbyid-znunk.

compile 'com.jakewharton:butterknife:7.0.1'

 

  @Override public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.simple_activity);
    ButterKnife.bind(this);
    // TODO Use fields...
  }

ezis leginkabb egy egyszerusites, hogy annotaciok altal lesz a view
include-olva/injectalva/bindolva, vagy pl nem kell setOnClickListenert irnunk
egy view-re, eleg ennyi:

 

@OnClick(R.id.submit)
public void submit() {
  // TODO submit data to server...
}

 

Es, hogy ez hasznos-e vagy szebb lesz e a kod tole? Ki-ki dontse el...egyszerubb az biztos.

video:

 

Bovebben: http://jakewharton.github.io/butterknife/


Egyebkent az android annotations is hasonlo, csak lehet, hogy tobbet tud (gyorsabb fejlesztes iger, es egy rakat annotaciot), video errol is:

komment

Retrofit - okhttp es sima html oldalak lekerese

2016. április 01. 09:54 - lacas8282

Mostanaban ezek a hangzatos kulcsszavak, ha mesz allasinterjura ezeket erdemes ismerni:

dagger, dagger2, android annotations, realm, sugarORM, butterknife, okhttp, dependency injection, picasso, glide
es Jake Wharton nevet is erdemes ismernunk :)


Tehat ezeket erdemes ismerni, githubon fen is vannak, vagy sima google search es tadamm...Mindegyikrol van youtube video is, igy ha erdekes utananezel. Kesobb foglalkozom ezekkel is, de csak szukszavuan, ahogyan szoktam.

Tehat lassuk a retrofit-et eloszor a sorban, mi ez?

http://square.github.io/retrofit/

https://github.com/square/retrofit

A type-safe HTTP client for Android and Java

OK lassuk mi ez magyarul: Vegulis ezzel is GET, POST, PUT stb-t tudunk lekerni a netrol dolgokat, mint eddig, foleg json-ban, vagy akar xml-ben, stb amiben szeretnenk. Multipart entityt is tudunk egyebkent hasznalni, es persze sync es async lekeres is van.

Na de akkor miert jobb ez mint mas?

A lenyege, hogy egy interface valositja meg az egesz http lekerest, tehat:

public interface GitHubService {
  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);
}

 

Ez mar azert sokkal szebb, mint egy Volley nemde? Es nem csak szep, de sokkal gyorsabb is! Lasd elozo POST.

Meghivni ezt igy tudjuk majd akarhonnan:

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .build();

GitHubService service = retrofit.create(GitHubService.class);

 

Mit csinal ez az egesz?

Az interface-be annotaciokat aggatunk magara a fuggvenyre, ebbol tudja majd Retrofit baratunk, hogy mit kell tennie az adott URL-el.

Nezzuk az interface-t. Ez egy Web-service deklaralasa tulajdonkeppen. $GET("akarmi") kozli vele, hogy GET lesz a lekeres nem POST vagy mas. Nem terek ki a kulonbsegekre, feltetelezem, hogy minimalis html, php tudasod megvan ehhez...Az akarmi helyere az url-t irjuk, a BASE_URL nelkul. Tehat csak a veget:)
Itt azert mar lehet latni, hogyha "{user}" van a kodban, akkor az valami template cucc lesz, es bizony igyis van.
Ezt fogja a retrofit kicserelni a

@Path("user") String user

 

-ben megadott ertekkel.

Tehat eleg konnyu eddig a dolog, szep is jo is, gyors is, primitiv is.

Alapbol azt hiszem amugy json (gson) parser van alatta, igy azt fog visszaadni, en egyelore csak sima String request-et kertem le vele, mint Volleynal. Teszteles miatt.

Nezzuk egy konkretabb peldat, mert nem nagyon van a neten, vagy stackoverflowon sem, nekem kellett ezt a megoldast megszulnom.

TEHAT:

Android Studioban dependencies koze:

compile 'com.squareup.retrofit2:retrofit:2.0.1'
compile 'com.squareup.retrofit2:converter-gson:2.0.0'
compile 'com.squareup.retrofit2:converter-scalars:2.0.0'
compile 'com.squareup.okhttp3:logging-interceptor:3.0.1'


Majd Sync Gradle. Ilyenkor ugye letolti ezeket es inkludolja a projectbe. Azaz ezek utan elerheted a funkcioit.
Retrofit 2.0.1 a legujabb library ezert azt importolom. gson converter nekunk elvileg nem kell, mert a scalars-al sima string koverziot akarunk lekerni majd.

Es van a logging-interceptor, ami ahhoz kell, hogy loggolni tudd, hogy miket csinal retrofit a hatterben. Itt eleve szivtam a verziokkal, mert a 3.0.1-ben teljesen mas kod van ugye, mint mondjuk az elozoekben, igy nem volt teljesen kompatibilis nehany esetben az input output a fuggvenyeknel. Vegulis kifiloztam, hogy ez kell nekem.

Elso korben azert kell loggolni minden szart LogCat-re retrofittel, mert nem biztos, hogy az osszes annotaciot megertetted.

Alapbol amugy a retrofit POJO modellekkel dolgozik. POJO: Plain Old Java Object, kb igy nez ki:

import com.google.gson.annotations.SerializedName;

public class UserPass {

@SerializedName("name")
public String name;
@SerializedName("remember")
public String remember;
@SerializedName("password")
public String password;

public UserPass(String name, String remember, String password) {
this.name = name;
this.remember = remember;
this.password = password;
}

}


OK, ezt nem tudom miert irtam ide, mert a mostani peldanal nem fontos, de ha tnaulni akarsz akkor igen:)
Mint emlitettem nem fogok POJO-t es annotaciokat hasznalni itt.

POJO generator online


Nezzuk tovabb, en egy sima http lekerest akarok, nem akarok json-t, kutyafulet, sima stringet akarok vissza. Es POST-olni szeretnek egy sima stringet, nem POJO-t.

Nezzunk egy sajat service-t, ez a chat szervize lesz, vagyis nem teljesen, de majdnem

public interface ChatHttpService {

@GET("/")
Call<ResponseBody> getBase();

@GET("/client_start")
Call<ResponseBody> getClientStart(@Header("Cookie") String cookie);

@FormUrlEncoded
@POST("/belepes")
Call<ResponseBody> getLogin(@Header("Cookie") String cookie, @Field("username") String username, @Field("remember") String remember, @Field("password") String password);
...

}

 

Tehat ez az interface reprezentalja nalunk a chat web-service-t, de ez esetben ne nevezzuk Service-nek, mert en service-nek json-t, xml-t, etc-t hivom. Itt viszont sima html-t kapunk majd vissza.

$GET-el get-el fogunk kuldeni "/"-el pedig a sima BASE_URL-t. ResponseBody-t akarunk visszakapni, nem sajat class-t. Nezzuk a tobbi lekerest.

Ugye ez 3 lekeres. $GET es $POST is van benne. A masodikban mar van egy uj dolog.

@Header("Cookie") String cookie

 

Itt tulajdonkeppen a header-eket tudjuk atadni majd a lekeresbe, termesezetesen statikus $Headers annotacio is van, de nekem dinamikus kellett. A statikus $Headers annotacio a fuggveny fele kerul, nem a fuggveny bemenetere, ahogy a dinamikus.

A kov lekeres egy POST tipusu, formurlencoded, es ujdonsag benne

@Field("username") String username

 

itt azt mondjuk meg mit akarunk post-olni. Tehat magat a data-t.

Ok le van gyartva az interface es megvannak az url-ek, most hogyan tovabb?

Hat kerlek szep, kell nyilvan egy retrofit instance valahol az alkalmazasunkba, es be kell allitani rajta dolgokat, nezzuk is hogyan tortenik ez meg

public static ChatHttpService getRetrofitService(boolean useProxy) {

ChatHttpUrls.useProxy = useProxy;

if (retrofit == null) {
OkHttpClient.Builder clientBuilder = new OkHttpClient().newBuilder();

if (useClientLogging) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
clientBuilder.addInterceptor(logging);
}

if (useProxy) {
Proxy p = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("94.23.85.67", 80)); //Spain-fast http
clientBuilder.proxy(p);
}

Logger.E("useProxy:" + useProxy);

client = clientBuilder.build();

retrofit = new Retrofit.Builder()
.baseUrl(ChatHttpUrls.BASE_URL)
.client(client)
//.addConverterFactory(GsonConverterFactory.create())
.addConverterFactory(ScalarsConverterFactory.create())
.build();

chatHttpService = retrofit.create(ChatHttpService.class);
}

return chatHttpService;
}

En egyelore igy oldottam meg, nem tul elegans, de tesztre jo.
A retrofittel egy okhttp-t hasznalunk majd kliensnek. Bovebb leiras errol:

 

https://github.com/square/okhttp/wiki

Amennyiben loggolni akarjuk, hogy miket kuldozget el es kap az okhttp/retrofit, ez esetben hozza kell adnunk egy interceptort. Ennek kulonbozo szintjei vannak, annak fuggvenyeben mit akarunk a LogCat-en viszont latni.

Proxy-val is tudunk csatlakozni ra, lasd a useProxy feltetelt. Vegul egy build()-el es builder pattern-el megkapjuk magat az object-et. Johet a retrofit, Szinten buildert hasznal, beallitjuk a BASE_URL-t, tehat pl http://valami.hu, majd hozzaaadjuk a client-t. Converterfactory-val hozzaadjuk a GSON helyett a Scalarst, mivel nem gson kell most nekunk. Majd create-el generaljuk a Service classt.

Ezek utan a service-t meg tudjuk hivni, es a service-ben levo url-eket is le tudjuk kerni, pl.:

private void loginPart1(final ValueGetCallback<String> callback) {

Call<ResponseBody> result = ChatHttpUrls.getRetrofitService(useProxy).getBase();

result.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {

if (response.isSuccessful()) {

try {
String html = response.headers().toString() + new String(response.body().bytes(), "UTF-8");

php_sess_id = stringCutter.getPHP_SESSIONID(html);

if (php_sess_id.equals("")) {
callback.error("Err 1/3 Sessid null");
} else {
callback.ok("1/3 ok");
//tovabb...
}


} catch (IOException e) {
e.printStackTrace();
}
} else {
callback.error("Err 1/3 response not succesfull");
}

}

@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
t.printStackTrace();
callback.error("Err 1/3 " + t.toString());
}
});
}

 

 En egy sajat callback-et is hasznaltam itt, ez igy nez ki:

public interface ValueGetCallback<T> {

void ok(T result);

void error(T err);
}

 

Szoval, a callback baromi egyszeru, ugye String-et fogunk itt hasznalni, tehat T-t helyettesitjuk arra, majd van egy ok, es egy error callback. Ezeket a MainActivityben vagy akarhol mashol hasznosithatjuk.
Nezzuk tovabb:
 

   Call<ResponseBody> result = ChatHttpUrls.getRetrofitService(useProxy).getBase();

 

useProxy-val eldontjuk akarunk e http proxy-t hasznalni, feltetelezem tudod mi a proxy, igy nem teszek errol se emlitest, most a retrofitet targyaljuk ki.:) (de a lenyeg, hogy http-re http proxy kell, es vannak free proxy list oldalak, sock-ra pedig sock proxy, azonban eleg lassuak, es nem nagyon erdemes hasznalni, nehez talalni oket. azert hasznaljuk oket, hogy ne a sajat ip-nkkel csatlakozzunk vmihez)

getBase()-el lekerjuk ugye a sima BASE_URL-t, valami.hu-t

result.enqueue(new Callback<ResponseBody>()

 


-el pedig elinditjuk a lekerest es egy callback-el megkapjuk, ha a lekeres sikeres volt e vagy errort dobott.

 Ezeket le tudjuk kezelni ott.

En ide ugye sajat callback-et raktam ok es error funkciokkal, ezeket hivom meg egy sima String-el, hogy mi tortent.

 String html = response.headers().toString() + new String(response.body().bytes(), "UTF-8");

Ha a lekeres sikerult, akkor a response.headers() -el tudjuk megnezni milyen headereket
adott vissza, ez akkor jo pl, ha parse-olni akarjuk eztis, pl PHP_SESSID kell.

 

 new String(response.body().bytes(), "UTF-8")

 

O pedig siman a responsebody bytes-jaibol general egy String-et UTF8 kodolassal.
Tehat maga a html oldal lesz string-ben.

Ok, hat egyelore ennyi, hogy hogyan hasznaljuk a retrofitet/okhttp-t arra, hogy html oldalakat kerjunk le.

Nagyon sokmindenre lehet a retrofitet hasznalni, json, xml, multipart, upload files, stb.
Ha erdekel mindennek utananezel, tenyleg nem nehez ez az egesz.

Es gyorsabb, szebb!

A lenyeg kimaradt: 5+2 weboldal html tartalmat 600-700 ms alatt kerte le. Ez eleg jo nemigaz?

komment
Címkék: retrofit

Volley vs AsyncTask

2016. április 01. 09:19 - lacas8282

Ok, ebben a postban a Volley-rol lesz szo. Ugye kezdetben mindenki Thread-eket hasznal, vagy AsyncTask-et, esetleg egy UrlConnection-t, hogy letoltsunk valami tartalmat a nettol.

Ezzel az a gond, hogy lassú és a kódolása is körülményes.

Szerencsére itt van nekunk a Volley, ami egy Google talalmany, jobb esetben mar ezt hasznalod, meg jobban pedig a retrofit library-t. A Volley egy HTTP library, mig a retrofit is hasonlo, de inkabb json-ra van alapbol fokuszalva. Mindenesetre meg lehet oldani, hogy sima stringet huzzon le ott is.

A Retrofitrol a kovetkezo post-ban beszelek, most ugyanis van idom blogolgatni, nincs nagyon munka.

Tehat a Volley elonyei:

Nem kell felesleges kodokat begepelnunk, es van hozza mar egy callback is, ezt sem kell megirnunk. (callback = interface)

Persze elso korben,, ha csak tanulasi celzattal tesszuk erdemes lehet AsyncTask/Callback/Http(s)UrlConnection combokat hasznalni, hogy azert lassuk mi megy vegbe.

Lassunk egy Volley kodot, teszem azt egy sima GET-re:

 

public void sendGet(String url, final String referer, final String cookie,
final HttpValueGetCallback<String> callback) {

StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);

StringRequest request = new StringRequest(Request.Method.GET, url,
new Response.Listener<String>()
{
@Override
public void onResponse(String response) {
if (callback!=null) callback.gotValueBack( responseHeaders.toString() +response);
}
},
new Response.ErrorListener()
{
@Override
public void onErrorResponse(VolleyError error) {
Logger.D("ERROR","error => "+error.toString());
if (callback!=null) callback.gotValueBack("ERR");
}
}
)
{

@Override
protected Response<String> parseNetworkResponse(NetworkResponse response) {
responseHeaders = response.headers;
return super.parseNetworkResponse(response);
}

@Override
public Map<String, String> getHeaders() {
Map<String, String> params = new HashMap<>();
if (referer!=null) params.put("Referer", referer);
if (cookie!=null) params.put("Cookie", cookie);

return params;
}
};

request.setRetryPolicy(
new DefaultRetryPolicy(
DefaultRetryPolicy.DEFAULT_TIMEOUT_MS,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));

queue.add(request);

}

En letrehoztam egy callback-et is, hogy egy sajat osztalybol siman
kapjak visszateresi erteket. A callback egyszeru:
public interface HttpValueGetCallback<T> {
void gotValueBack(T result);
}


A queue-nk pedig:

queue = Volley.newRequestQueue(ChathuStartActivity.mContext);


Mint lathato ugyanugy be tudjuk allitgatni a header-eket, cookie-kat, referrert, akarmit amit csak akarunk. 

Bovebb leiras, angolul itt talalhatunk az asynctasks vs volley vs retrofit-rol

http://instructure.github.io/blog/2013/12/09/volley-vs-retrofit/

En egyelore a chat6-ba refaktoraltam a kodot, hogy sima Async-ok es UrlConnection-ok helyett, ezzel kerem le, de a valtozas maris megtortent. Kb 1.5-2-szer gyorsabb egy-egy lekeres, mint az eredeti megoldas es kicsit szebb megoldas is, mint ganyolgatni.

Volleyban van

StringRequest

ImageRequest

JsonObjectRequest

JsonArrayRequest

JsonRequest

Bővebben: http://developer.android.com/training/volley/request.html

Amit elfelejtettem: ugye mindenki Android Studioban kodol mar, aki ertelmesebb, tehat a dependencies-be ezt rakd hozza:

compile 'com.android.volley:volley:1.0.0'


Emlekezz: mindig hasznalj kesz megoldasokat, ne ird meg azt, amik mar meg vannak irva.

Azt hiszem nem esett szo meg a blogon a Picasso vs Glide koönyvtarakrol sem, majd ennek is szentelek a kozeljovoben egy post-ot.

komment
Címkék: asynctask volley
süti beállítások módosítása